/
js2coffee.coffee
142 lines (123 loc) · 3.94 KB
/
js2coffee.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
TransformerBase = require('./lib/transforms/base')
BuilderBase = require('./lib/builder/base')
Builder = require('./lib/builder')
{ buildError } = require('./lib/helpers')
###*
# # Js2coffee API
###
module.exports = js2coffee = (source, options) ->
js2coffee.build(source, options).code
###*
# js2coffee() : js2coffee(source, [options])
# Compiles JavaScript into CoffeeScript.
#
# output = js2coffee.build('a = 2', {});
#
# output.code
# output.ast
# output.map
# output.warnings
#
# All options are optional. Available options are:
#
# ~ filename (String): the filename, used in source maps and errors.
# ~ comments (Boolean): set to `false` to disable comments.
#
# Here's what it does:
#
# 1. Parse code into a JS AST (`.parseJS()`)
# 2. Mutate the JS AST into a CoffeeScript AST (`.transform()`)
# 3. Render the AST into CoffeeScript (`.generate()`)
###
js2coffee.build = (source, options = {}) ->
options.filename ?= 'input.js'
options.indent ?= 2
options.source = source
ast = js2coffee.parseJS(source, options)
{ast, warnings} = js2coffee.transform(ast, options)
{code, map} = js2coffee.generate(ast, options)
{code, ast, map, warnings}
###*
# parseJS() : js2coffee.parseJS(source, [options])
# Parses JavaScript code into an AST via Esprima.
# Returns a JavaScript AST. Throws an error if parsing can't continue.
#
# try
# ast = js2coffee.parseJS('var a = 2;')
# catch err
# ...
###
js2coffee.parseJS = (source, options = {}) ->
try
Esprima = require('esprima')
Esprima.parse(source, loc: true, range: true, comment: true)
catch err
throw buildError(err, source, options.filename)
###*
# transform() : js2coffee.transform(ast, [options])
# Mutates a given JavaScript syntax tree `ast` into a CoffeeScript AST.
#
# ast = js2coffee.parseJS('var a = 2;')
# result = js2coffee.transform(ast)
#
# result.ast
# result.warnings
#
# This performs a few traversals across the tree using traversal classes
# (TransformerBase subclasses).
#
# These transformations will need to be done in multiple passes. The earlier
# steps (function, comment, etc) will make drastic modifications to the tree
# that the other transformations will need to pick up.
###
js2coffee.transform = (ast, options = {}) ->
ctx = {}
run = (classes) -> TransformerBase.run(ast, options, classes, ctx)
comments = not (options.comments is false)
# Injects comments into the AST as BlockComment and LineComment nodes.
run [
require('./lib/transforms/comments')
] if comments
# Moves named functions to the top of the scope.
run [
require('./lib/transforms/functions')
]
# Everything else -- these can be done in one step without any side effects.
run [
require('./lib/transforms/exponents')
require('./lib/transforms/ifs')
require('./lib/transforms/iife')
require('./lib/transforms/literals')
require('./lib/transforms/loops')
require('./lib/transforms/members')
require('./lib/transforms/objects')
require('./lib/transforms/binary')
require('./lib/transforms/empty_statements')
require('./lib/transforms/others')
require('./lib/transforms/precedence')
require('./lib/transforms/returns')
require('./lib/transforms/switches')
require('./lib/transforms/unsupported')
]
# Consolidate nested blocks -- block nesting can be a side effect of the
# transformations above
run [
require('./lib/transforms/blocks')
]
{ ast, warnings: ctx.warnings }
###*
# generate() : js2coffee.generate(ast, [options])
# Generates CoffeeScript code from a given CoffeeScript AST. Returns an object
# with `code` (CoffeeScript source code) and `map` (source mapping object).
#
# ast = js2coffee.parse('var a = 2;')
# ast = js2coffee.transform(ast)
# {code, map} = generate(ast)
###
js2coffee.generate = (ast, options = {}) ->
new Builder(ast, options).get()
###*
# version : js2coffee.version
# The version number
###
js2coffee.version = require('./package.json').version