edb281300e4f05a2e3afd4903edb4a59c10fb045
[platform/framework/web/crosswalk-tizen.git] /
1 # Rocambole [![Build Status](https://secure.travis-ci.org/millermedeiros/rocambole.png?branch=master)](https://travis-ci.org/millermedeiros/rocambole)
2
3 ![rocambole](https://raw.github.com/millermedeiros/rocambole/master/rocambole.jpg)
4
5 Recursively walk and add extra information/helpers to [Esprima / Mozilla
6 SpiderMonkey Parser API](http://esprima.org/doc/index.html#ast) compatible AST.
7
8 The main difference between other tools is that it also keeps information about
9 tokens and white spaces and it is meant to be used to transform the tokens and
10 not the string values itself.
11
12 This library is specially useful for non-destructive AST manipulation.
13
14
15 ## Inspiration
16
17 This module was heavily inspired by
18 [node-falafel](https://github.com/substack/node-falafel) and
19 [node-burrito](https://github.com/substack/node-burrito) but I needed more
20 information than what is currently available on falafel (specially about
21 tokens, empty lines and white spaces) and also needed to do the node traversing
22 on the opposite order (start from leaf nodes). The amount of changes required
23 to introduce the new features and the differences on the concept behind the
24 tool justified a new project.
25
26 It was created mainly to be used on
27 [esformatter](https://github.com/millermedeiros/esformatter/).
28
29
30
31 ## Extra Tokens
32
33 Besides all the regular tokens returned by `esprima` we also add a few more
34 that are important for non-destructive transformations:
35
36  * `WhiteSpace`
37    - Can store multiple white spaces (tabs are considered white space, line
38      breaks not). Important if you want to do non-destructive replacements that
39      are white-space sensitive.
40    - Multiple subsequent white spaces are treated as a single token.
41  * `LineBreak`
42  * `LineComment`
43  * `BlockComment`
44
45 It's way easier to rebuild the JS string if the tokens already have line breaks
46 and comments. It's also easier to identify if previous/next/current token is a
47 LineBreak or Comment (sometimes needed for non-destructive transformations).
48
49 Rocambole structure might change in the future to keep the extraneous tokens
50 outside the `tokens` array and also add an option to toggle the behavior.
51 ([issue #7](https://github.com/millermedeiros/rocambole/issues/7))
52
53
54 ## Extra Properties
55
56 Each Node have the following extra properties/methods:
57
58   - `parent` : Node|undefined
59   - `toString()` : string
60   - `next` : Node|undefined
61   - `prev` : Node|undefined
62   - `depth` : Number
63   - `startToken` : Token
64   - `endToken` : Token
65
66 Each token also have:
67
68  - `prev` : Token|undefined
69  - `next` : Token|undefined
70
71 BlockComment also have:
72
73   - `originalIndent`: String|undefined
74
75 To get a better idea of the generated AST structure try out
76 [rocambole-visualize](http://piuccio.github.io/rocambole-visualize/).
77
78
79 ## Linked List
80
81 You should **treat the tokens as a linked list instead of reading the
82 `ast.tokens` array** (inserting/removing items from a linked list is very cheap
83 and won't break the loop). You should grab a reference to the `node.startToken`
84 and get `token.next` until you find the desired token or reach the end of the
85 program. To loop between all tokens inside a node you can do like this:
86
87 ```js
88 var token = node.startToken;
89 while (token !== node.endToken.next) {
90     doStuffWithToken(token);
91     token = token.next;
92 }
93 ```
94
95 The method `toString` loops through all tokens between `node.startToken` and
96 `node.endToken` grabbing the `token.raw` (used by comments) or `token.value`
97 properties. To implement a method similar to falafel `update()` you can do
98 this:
99
100 ```js
101 function update(node, str){
102     var newToken = {
103         type : 'Custom', // can be anything (not used internally)
104         value : str
105     };
106     // update linked list references
107     if ( node.startToken.prev ) {
108         node.startToken.prev.next = newToken;
109         newToken.prev = node.startToken.prev;
110     }
111     if ( node.endToken.next ) {
112         node.endToken.next.prev = newToken;
113         newToken.next = node.endToken.next;
114     }
115     node.startToken = node.endToken = newToken;
116 }
117 ```
118
119
120 ## Helpers
121
122 I plan to create helpers as separate projects. For now I'm adding the helpers
123 on the [esformatter util
124 package](https://github.com/millermedeiros/esformatter/tree/master/lib/util)
125 but I plan to extract the generic ones.
126
127  - [rocambole-token](https://github.com/millermedeiros/rocambole-token): helpers for token manipulation
128
129
130
131 ## API
132
133
134 ### rocambole.parse
135
136 Parses a string and instrument the AST with extra properties/methods.
137
138 ```js
139 var rocambole = require('rocambole');
140 var ast = rocambole.parse(string);
141 console.log( ast.startToken );
142 // to get a string representation of all tokens call toString()
143 console.log( ast.toString() );
144 ```
145
146
147 ### rocambole.moonwalk
148
149 The `moonwalk()` starts at the leaf nodes and go down the tree until it reaches
150 the root node (`Program`). Each node will be traversed only once.
151
152 ```js
153 rocambole.moonwalk(ast, function(node){
154     if (node.type == 'ArrayExpression'){
155         console.log( node.depth +': '+ node.toString() );
156     }
157 });
158 ```
159
160 Traverse order:
161
162 ```
163  Program [#18]
164  `-FunctionDeclaration [#16]
165    |-BlockStatement [#14]
166    | |-IfStatement [#12]
167    | | |-BynaryExpression [#9]
168    | | | |-Identifier [#4]
169    | | | `-Literal [#5]
170    | | `-BlockStatement [#10]
171    | |   `-ExpressionStatement [#6]
172    | |     `-AssignmentExpression [#3]
173    | |       |-Identifier [#1 walk starts here]
174    | |       `-Literal [#2]
175    | `-VariableDeclaration [#13]
176    |   `-VariableDeclarator [#11]
177    |     |-Identifier [#7]
178    |     `-Literal [#8]
179    `-ReturnStatement [#17]
180      `-Identifier [#15]
181 ```
182
183 This behavior is very different from node-falafel and node-burrito.
184
185
186 ### rocambole.recursive
187
188 It loops through all nodes on the AST starting from the root node (`Program`),
189 similar to `node-falafel`.
190
191 ```js
192 rocambole.recursive(ast, function(node){
193     console.log(node.type);
194 });
195 ```
196
197
198 ## Popular Alternatives
199
200  - [burrito](https://github.com/substack/node-burrito)
201  - [falafel](https://github.com/substack/node-falafel)
202
203
204
205 ## Unit Tests
206
207 Besides the regular unit tests we also use
208 [istanbul](https://github.com/yahoo/istanbul) to generate code coverage
209 reports, tests should have at least 95% code coverage for statements, branches
210 and lines and 100% code coverage for functions or travis build will fail.
211
212 We do not run the coverage test at each call since it slows down the
213 performnace of the tests and it also makes it harder to see the test results.
214 To execute tests and generate coverage report call `npm test --coverage`, for
215 regular tests just do `npm test`.
216
217 Coverage reports are not committed to the repository since they will change at
218 each `npm test --coverage` call.
219
220
221
222 ## License
223
224 MIT
225
226
227
228 ## Changelog
229
230 ### v0.3.6 (2014/06/23)
231
232  - really handle sparse arrays (eg. `[,]`), fixes moonwalk. (#15)
233
234 ### v0.3.5 (2014/06/23)
235
236  - handle sparse arrays (eg. `[,]`). (#15)
237
238 ### v0.3.4 (2014/06/23)
239
240  - only add `BlockComment.originalIndent` if `WhiteSpace` is on the start of
241    a line.
242
243 ### v0.3.3 (2014/04/26)
244
245  - add `toString` to empty programs AST (#16)
246
247 ### v0.3.2 (2014/01/17)
248
249  - exports `BYPASS_RECURSION` (#8)
250  - fix error if input is empty (#12)
251  - support anything that implements `toString()` as input (#13)
252
253 ### v0.3.1 (2013/12/15)
254
255  - fix `originalIndent` on `BlockComment` when prev token is not `WhiteSpace`.
256
257 ### v0.3.0 (2013/12/15)
258
259  - add `originalIndent` to `BlockComment` (#11)
260
261 ### v0.2.3 (2013/01/08)
262
263  - improve `rocambole.parse()` performance by 4500%. (#4)
264  - improve `rocambole.moonwalk()` performance by 11000%.
265
266 ### v0.2.2 (2012/12/19)
267
268  - fix consecutive comments before start of program. (#3)
269
270 ### v0.2.1 (2012/12/13)
271
272  - fix `loc` info on `WhiteSpace` and `LineBreak` tokens. (#2)
273
274 ### v0.2.0 (2012/12/09)
275
276  - Deprecated:
277    - `token.before()`
278    - `token.after()`
279    - `token.remove()`
280    - `node.getTokens()`
281    - `ast.nodes`
282  - avoid recursion over comments.
283  - fix weird bug on esformatter introduced on v0.1.1 related to `token._ast`
284    property.
285
286 ### v0.1.1 (2012/12/08)
287
288  - Improve token manipulation methods behavior (`before`, `after`, `remove`)
289
290 ### v0.1.0 (2012/12/06)
291
292  - Initial release
293