@@ -257,14 +257,53 @@ exports.Base = class Base
257
257
lastNode : (list ) ->
258
258
if list .length is 0 then null else list[list .length - 1 ]
259
259
260
- # `toString` representation of the node, for inspecting the parse tree.
260
+ # Debugging representation of the node, for inspecting the parse tree.
261
261
# This is what `coffee --nodes` prints out.
262
262
toString : (idt = ' ' , name = @constructor .name ) ->
263
263
tree = ' \n ' + idt + name
264
264
tree += ' ?' if @soak
265
265
@eachChild (node ) -> tree += node .toString idt + TAB
266
266
tree
267
267
268
+ # Plain JavaScript object representation of the node, that can be serialized
269
+ # as JSON. This is used for generating an abstract syntax tree (AST).
270
+ # This is what the `nodes` option in the Node API returns.
271
+ toPlainObject : ->
272
+ # We try to follow the [Babel AST spec](https://github.com/babel/babel/blob/master/packages/babylon/ast/spec.md)
273
+ # as closely as possible, for improved interoperability with other tools.
274
+ obj =
275
+ type : @constructor .name
276
+ # Convert `locationData` to Babel’s style.
277
+ loc :
278
+ start :
279
+ line : @locationData .first_line
280
+ column : @locationData .first_column
281
+ end :
282
+ line : @locationData .last_line
283
+ column : @locationData .last_column
284
+
285
+ # Add serializable properties to the output. Properties that aren’t
286
+ # automatically serializable (because they’re already a primitive type)
287
+ # should be handled on a case-by-case basis in child node classes’ own
288
+ # `toPlainObject` methods.
289
+ for property, value of this
290
+ continue if property in [' locationData' , ' children' ]
291
+ continue if value is undefined # Don’t skip `null` or `false` values.
292
+ if typeof value is ' boolean' or typeof value is ' number' or typeof value is ' string'
293
+ obj[property] = value
294
+
295
+ # Work our way down the tree. This is like `eachChild`, except that we
296
+ # preserve the child node name, and arrays.
297
+ for attr in @children when @ [attr]
298
+ if Array .isArray (@ [attr])
299
+ obj[attr] = []
300
+ for child in flatten [@ [attr]]
301
+ obj[attr].push child .unwrap ().toPlainObject ()
302
+ else
303
+ obj[attr] = @ [attr].unwrap ().toPlainObject ()
304
+
305
+ obj
306
+
268
307
# Passes each child to a function, breaking when the function returns `false`.
269
308
eachChild : (func ) ->
270
309
return this unless @children
0 commit comments