ruby2js 2.1.24 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +91 -50
  3. data/lib/ruby2js/converter/args.rb +10 -0
  4. data/lib/ruby2js/converter/block.rb +1 -1
  5. data/lib/ruby2js/converter/case.rb +1 -1
  6. data/lib/ruby2js/converter/casgn.rb +6 -1
  7. data/lib/ruby2js/converter/class.rb +7 -2
  8. data/lib/ruby2js/converter/class2.rb +167 -0
  9. data/lib/ruby2js/converter/def.rb +54 -6
  10. data/lib/ruby2js/converter/defs.rb +11 -5
  11. data/lib/ruby2js/converter/dstr.rb +26 -0
  12. data/lib/ruby2js/converter/for.rb +3 -3
  13. data/lib/ruby2js/converter/hash.rb +31 -6
  14. data/lib/ruby2js/converter/if.rb +1 -1
  15. data/lib/ruby2js/converter/ivasgn.rb +5 -2
  16. data/lib/ruby2js/converter/masgn.rb +41 -4
  17. data/lib/ruby2js/converter/send.rb +71 -10
  18. data/lib/ruby2js/converter/super.rb +21 -8
  19. data/lib/ruby2js/converter/vasgn.rb +5 -1
  20. data/lib/ruby2js/converter/while.rb +13 -0
  21. data/lib/ruby2js/converter.rb +32 -5
  22. data/lib/ruby2js/es2015/strict.rb +3 -0
  23. data/lib/ruby2js/es2015.rb +5 -0
  24. data/lib/ruby2js/es2016/strict.rb +3 -0
  25. data/lib/ruby2js/es2016.rb +5 -0
  26. data/lib/ruby2js/es2017/strict.rb +3 -0
  27. data/lib/ruby2js/es2017.rb +5 -0
  28. data/lib/ruby2js/execjs.rb +1 -1
  29. data/lib/ruby2js/filter/camelCase.rb +1 -1
  30. data/lib/ruby2js/filter/functions.rb +49 -2
  31. data/lib/ruby2js/filter/vue.rb +66 -45
  32. data/lib/ruby2js/strict.rb +3 -0
  33. data/lib/ruby2js/version.rb +3 -3
  34. data/lib/ruby2js.rb +41 -0
  35. data/ruby2js.gemspec +3 -1
  36. metadata +10 -6
  37. data/lib/ruby2js/filter/angular-resource.rb +0 -25
  38. data/lib/ruby2js/filter/angular-route.rb +0 -44
  39. data/lib/ruby2js/filter/angularrb.rb +0 -604
  40. data/lib/ruby2js/filter/strict.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf2d8e7dd8a01998e41d419eca3fdf1c37f596ed
4
- data.tar.gz: '018be6424d191987b685162c9c8a9328d6375650'
3
+ metadata.gz: 0b1bcb8158bb81cdfafcf70eb22f84ad5dc195b1
4
+ data.tar.gz: '08d8a3e28b785aa35c52317b86f5749cebce31de'
5
5
  SHA512:
6
- metadata.gz: 7da7b8a123718cb0609a34076fa02865561f7b56a4f394c1f8919125c8d6a79ebccc65af70c4429db55783a000494f6ff6b5fbd24ffb1dd1cc2981aaaae87bda
7
- data.tar.gz: 0acc39d55555d57484e8c73ec824d40ed8b17ef5881f5a78511228bcdd74b07e4e9522bf1a6d05b52adb8fb21d06f8d7f21435d2c875e81c9343d92010a220f6
6
+ metadata.gz: 3c4946688aa439dd57d84de8115f1fbee1155a7d42227acf6ab2633ce6e6eb8c6e7f0fc7c7e322badd831642e856cc1991439c97beda892cd212f99e3cc8aa51
7
+ data.tar.gz: d8e945b65e77e4aacd05e36864f505c7cd454f8b00f788d37e7254f642df860cde0c9a6ebee44d854ba6c706a1c768d3c4dbe5c3e65ec86df660648b5ccf5ccd
data/README.md CHANGED
@@ -9,12 +9,35 @@ Minimal yet extensible Ruby to JavaScript conversion.
9
9
  Description
10
10
  ---
11
11
 
12
- The base package maps Ruby syntax to JavaScript semantics. For example,
13
- a Ruby Hash literal becomes a JavaScript Object literal. Ruby symbols
14
- become JavaScript strings. Ruby method calls become JavaScript function
15
- calls IF there are either one or more arguments passed OR parenthesis are
16
- used, otherwise Ruby method calls become JavaScript property accesses.
17
- By default, methods and procs return `undefined`.
12
+ The base package maps Ruby syntax to JavaScript semantics.
13
+ For example:
14
+
15
+ * a Ruby Hash literal becomes a JavaScript Object literal
16
+ * Ruby symbols become JavaScript strings.
17
+ * Ruby method calls become JavaScript function calls IF
18
+ there are either one or more arguments passed OR
19
+ parenthesis are used
20
+ * otherwise Ruby method calls become JavaScript property accesses.
21
+ * by default, methods and procs return `undefined`
22
+ * splats are handled (may need to try around or read transliteration_spec)
23
+ * ruby string interpolation is expanded into string + operations
24
+ * `and` and `or` become `&&` and `||`
25
+ * `a ** b` becomes `Math.pow(a,b)`
26
+ * `<< a` becomes `.push(a)`
27
+ * `unless` becomes `if !`
28
+ * `until` becomes `while !`
29
+ * `case` and `when` becomes `switch` and `case`
30
+ * ruby for loops become js for loops
31
+ * `(1...4).step(2){` becomes `for (var i = 1; i < 4; i += 2) {`
32
+ * `x.forEach { next }` becomes `x.forEach(function() {return})`
33
+ * `lambda {}` and `proc {}` becomes `function() {}`
34
+ * `class Person; end` becomes `function Person() {}`
35
+ * instance methods become prototype methods
36
+ * instance variables become underscored, `@name` becomes `this._name`
37
+ * self is assigned to this is if used
38
+ * Any block becomes and explicit argument `new Promise do; y(); end` becomes `new Promise(function() {y()})`
39
+ * regular expressions are mapped to js
40
+ * `raise` becomes `throw`
18
41
 
19
42
  Ruby attribute accessors, methods defined with no parameters and no
20
43
  parenthesis, as well as setter method definitions, are
@@ -47,6 +70,18 @@ require 'ruby2js/filter/functions'
47
70
  puts Ruby2JS.convert('"2A".to_i(16)')
48
71
  ```
49
72
 
73
+ Enable ES2015 support:
74
+
75
+ ```ruby
76
+ puts Ruby2JS.convert('"#{a}"', eslevel: 2015)
77
+ ```
78
+
79
+ Enable strict support:
80
+
81
+ ```ruby
82
+ puts Ruby2JS.convert('a=1', strict: true)
83
+ ```
84
+
50
85
  With [ExecJS](https://github.com/sstephenson/execjs):
51
86
  ```ruby
52
87
  require 'ruby2js/execjs'
@@ -140,9 +175,6 @@ In general, making use of a filter is as simple as requiring it. If multiple
140
175
  filters are selected, they will all be applied in parallel in one pass through
141
176
  the script.
142
177
 
143
- * <a id="strict" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/strict.rb">strict</a>
144
- adds `'use strict';` to the output.
145
-
146
178
  * <a id="return" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/return.rb">return</a>
147
179
  adds `return` to the last expression in functions.
148
180
 
@@ -164,7 +196,9 @@ the script.
164
196
  * `.clear` becomes `.length = 0`
165
197
  * `.delete` becomes `delete target[arg]`
166
198
  * `.downcase` becomes `.toLowerCase`
167
- * `.each` becomes `forEach`
199
+ * `.each` becomes `for (i in ...) {}`
200
+ * `.each_key` becomes `Object.keys().forEach`
201
+ * `.each_value` becomes `for (i in ...) {}`
168
202
  * `.each_with_index` becomes `.forEach`
169
203
  * `.end_with?` becomes `.slice(-arg.length) == arg`
170
204
  * `.empty?` becomes `.length == 0`
@@ -200,6 +234,7 @@ the script.
200
234
  * `[n...m]` becomes `.slice(n,m)`
201
235
  * `[n..m]` becomes `.slice(n,m+1)`
202
236
  * `[/r/, n]` becomes `.match(/r/)[n]`
237
+ * `(1..2).each {|i| ...}` becomes `for (var i=1 i<=2; i+=1)`
203
238
  * `"string" * length` becomes `new Array(length + 1).join("string")`
204
239
  * `.sub!` and `.gsub!` become equivalent `x = x.replace` statements
205
240
  * `.map!`, `.reverse!`, and `.select` become equivalent
@@ -332,39 +367,6 @@ the script.
332
367
  * defaults the fourth parameter of $$.post to `"json"`, allowing Ruby block
333
368
  syntax to be used for the success function.
334
369
 
335
- * <a id="angularrb" href="https://github.com/rubys/ruby2js/blob/master/spec/angularrb.rb">angularrb</a>
336
-
337
- * maps Ruby `module` to `angular.module`
338
- * maps `filter`, `controller`, `factory`, and `directive` to calls to
339
- angular module functions.
340
- * maps `use` statements to formal arguments or array values (as
341
- appropriate) depending on the module function.
342
- * maps `watch` statements to calls to `$scope.$watch`.
343
- * tracks globals variable and constant references and adds additional
344
- implicit `use` statements
345
- * maps constant assignments in an angular module to a filter
346
- * maps class definitions in an angular module to a filter
347
- * within a controller or within a `link` method in a directive:
348
- * maps `apply`, `broadcast`, `digest`, `emit`, `eval`, `evalAsync`, and
349
- `parent` calls to `$scope` functions.
350
- * maps `apply!`, `broadcast!`, `digest!`, `eval!`, and `evalAsync!`
351
- calls to `$rootScope` functions.
352
- * maps `filter` calls to '$filter` calls.
353
- * maps `timeout` and `interval` calls with a block to `$timeout` and
354
- `$interval` calls where the block is passed as the first parameter.
355
-
356
- * <a id="angular-route" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/angular-routerb.rb">angular-route</a>
357
-
358
- * maps `case` statements on `$routeProvider` to angular.js module
359
- configuration.
360
- * adds implicit module `use` of `ngRoute` when such a `case` statement
361
- is encountered
362
-
363
- * <a id="angular-resource" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/angular-resource.rb">angular-resource</a>
364
- * maps `$resource.new` statements on `$resource` function calls.
365
- * adds implicit module `use` of `ngResource` when `$resource.new` calls
366
- are encountered
367
-
368
370
  * <a id="minitest-jasmine" href="https://github.com/rubys/ruby2js/blob/master/spec/minitest-jasmine.rb">minitest-jasmine</a>
369
371
  * maps subclasses of `Minitest::Test` to `describe` calls
370
372
  * maps `test_` methods inside subclasses of `Minitest::Test` to `it` calls
@@ -393,13 +395,52 @@ the script.
393
395
  and [wiki](https://github.com/rubys/wunderbar/blob/master/demo/wiki.rb) make
394
396
  use of the jquery filter.
395
397
 
396
- * [angularjs](https://github.com/rubys/wunderbar/blob/master/demo/angularjs.rb)
397
- makes use of the angular filters to implement the
398
- [angular.js tutorial](http://docs.angularjs.org/tutorial). This demo
399
- includes:
400
- * [view](https://github.com/rubys/wunderbar/blob/master/demo/views/index._html)
401
- * [partials](https://github.com/rubys/wunderbar/tree/master/demo/partials)
402
- * [js](https://github.com/rubys/wunderbar/tree/master/demo/js)
398
+ ES2015 support
399
+ ---
400
+
401
+ When option `eslevel: 2015` is provided, the following additional
402
+ conversions are made:
403
+
404
+ * `"#{a}"` becomes <code>\`${a}\`</code>
405
+ * `a = 1` becomes `let a = 1`
406
+ * `A = 1` becomes `const A = 1`
407
+ * `a, b = b, a` becomes `[a, b] = [b, a]`
408
+ * `a, (foo, *bar) = x` becomes `let [a, [foo, ...bar]] = x`
409
+ * `def f(a, (foo, *bar))` becomes `function f(a, [foo, ...bar])`
410
+ * `def a(b=1)` becomes `function a(b=1)`
411
+ * `def a(*b)` becomes `function a(...b)`
412
+ * `.each_key` becomes `for (i of ...) {}`
413
+ * `a(*b)` becomes `a(...b)`
414
+ * `"#{a}"` becomes <code>\`${a}\`</code>
415
+ * `lambda {|x| x}` becomes `(x) => {return x}`
416
+ * `proc {|x| x}` becomes `(x) => {x}`
417
+ * `a {|x|}` becomes `a((x) => {})`
418
+ * `class Person; end` becomes `class Person {}`
419
+
420
+ ES2015 class support includes constructors, super, methods, class methods,
421
+ instance methods, instance variables, class variables, getters, setters,
422
+ attr_accessor, attr_reader, attr_writer, etc.
423
+
424
+ Additionally, the `functions` filter will provide the following conversion:
425
+
426
+ * `Array(x)` becomes `Array.from(x)`
427
+ * `.inject(n) {}` becomes `.reduce(() => {}, n)`
428
+
429
+ ES2016 support
430
+ ---
431
+
432
+ When option `eslevel: 2016` is provided, the following additional
433
+ conversion is made:
434
+
435
+ * `a ** b` becomes `a ** b`
436
+
437
+ ES2017 support
438
+ ---
439
+
440
+ When option `eslevel: 2017` is provided, the following additional
441
+ conversion is made by the `functions` filter:
442
+
443
+ * `.each_entry` becomes `Object.entries().forEach`
403
444
 
404
445
  Picking a Ruby to JS mapping tool
405
446
  ---
@@ -9,5 +9,15 @@ module Ruby2JS
9
9
  handle :args do |*args|
10
10
  parse_all(*args, join: ', ')
11
11
  end
12
+
13
+ handle :mlhs do |*args|
14
+ if es2015
15
+ put '['
16
+ parse_all(*args, join: ', ')
17
+ put ']'
18
+ else
19
+ raise NotImplementedError, "destructuring requires ES2015"
20
+ end
21
+ end
12
22
  end
13
23
  end
@@ -58,7 +58,7 @@ module Ruby2JS
58
58
  # consisting of an anonymous function
59
59
  block ||= s(:begin)
60
60
  function = @ast.updated(:def, [nil, args, block])
61
- parse s(:send, *call.children, function)
61
+ parse s(@ast.children[0].type, *call.children, function), @state
62
62
  end
63
63
  end
64
64
  end
@@ -31,7 +31,7 @@ module Ruby2JS
31
31
  if scope
32
32
  vars = @vars.select {|key, value| value == :pending}.keys
33
33
  unless vars.empty?
34
- insert mark, "var #{vars.join(', ')}#{@sep}"
34
+ insert mark, "#{es2015 ? 'let' : 'var'} #{vars.join(', ')}#{@sep}"
35
35
  vars.each {|var| @vars[var] = true}
36
36
  end
37
37
  end
@@ -6,8 +6,13 @@ module Ruby2JS
6
6
 
7
7
  handle :casgn do |cbase, var, value|
8
8
  begin
9
- put "var "
9
+ if es2015
10
+ put "const "
11
+ else
12
+ put "var "
13
+ end
10
14
 
15
+ cbase ||= @rbstack.map {|rb| rb[var]}.compact.last
11
16
  (parse cbase; put '.') if cbase
12
17
 
13
18
  put "#{ var } = "; parse value
@@ -9,6 +9,11 @@ module Ruby2JS
9
9
  # NOTE: :prop and :method macros are defined at the bottom of this file
10
10
 
11
11
  handle :class do |name, inheritance, *body|
12
+ if es2015
13
+ parse @ast.updated(:class2)
14
+ return
15
+ end
16
+
12
17
  if inheritance
13
18
  init = s(:def, :initialize, s(:args), s(:super))
14
19
  else
@@ -48,7 +53,7 @@ module Ruby2JS
48
53
  # method: add to prototype
49
54
  s(:method, s(:attr, name, :prototype),
50
55
  :"#{m.children[0].to_s.chomp('!')}=",
51
- s(:block, s(:send, nil, :proc), *m.children[1..-1]))
56
+ s(:def, nil, *m.children[1..-1]))
52
57
  end
53
58
  end
54
59
 
@@ -57,7 +62,7 @@ module Ruby2JS
57
62
  # class property setter
58
63
  s(:prop, name, m.children[1].to_s[0..-2] =>
59
64
  {enumerable: s(:true), configurable: s(:true),
60
- set: s(:block, s(:send, nil, :proc), *m.children[2..-1])})
65
+ set: s(:def, nil, *m.children[2..-1])})
61
66
  elsif m.children[2].children.length == 0 and
62
67
  m.children[1] !~ /!/ and m.loc and m.loc.name and
63
68
  m.loc.name.source_buffer.source[m.loc.name.end_pos] != '('
@@ -0,0 +1,167 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (class2
5
+ # (const nil :A)
6
+ # (const nil :B)
7
+ # (...)
8
+
9
+ # NOTE: this is the es2015 version of class
10
+
11
+ handle :class2 do |name, inheritance, *body|
12
+ if name.type == :const and name.children.first == nil
13
+ put 'class '
14
+ parse name
15
+ else
16
+ parse name
17
+ put ' = class'
18
+ end
19
+
20
+ if inheritance
21
+ put ' extends '
22
+ parse inheritance
23
+ end
24
+
25
+ put " {"
26
+
27
+ body.compact!
28
+ while body.length == 1 and body.first.type == :begin
29
+ body = body.first.children
30
+ end
31
+
32
+ begin
33
+ class_name, @class_name = @class_name, name
34
+ class_parent, @class_parent = @class_parent, inheritance
35
+ @rbstack.push({})
36
+
37
+ post = []
38
+ skipped = false
39
+ body.each_with_index do |m, index|
40
+ put(index == 0 ? @nl : @sep) unless skipped
41
+ skipped = false
42
+
43
+ # intercept async definitions
44
+ if es2017 and m.type == :send and m.children[0..1] == [nil, :async]
45
+ child = m.children[2]
46
+ if child.type == :def
47
+ m = child.updated(:async)
48
+ elsif child.type == :defs and child.children[0].type == :self
49
+ m = child.updated(:asyncs)
50
+ end
51
+ end
52
+
53
+ if m.type == :def || m.type == :async
54
+ @prop = m.children.first
55
+
56
+ if @prop == :initialize
57
+ @prop = :constructor
58
+ m = m.updated(m.type, [@prop, *m.children[1..2]])
59
+ elsif not m.is_method?
60
+ @rbstack.last[@prop] = s(:self)
61
+ @prop = "get #{@prop}"
62
+ m = m.updated(m.type, [*m.children[0..1],
63
+ s(:autoreturn, m.children[2])])
64
+ elsif @prop.to_s.end_with? '='
65
+ @prop = @prop.to_s.sub('=', '').to_sym
66
+ @rbstack.last[@prop] = s(:self)
67
+ m = m.updated(m.type, [@prop, *m.children[1..2]])
68
+ @prop = "set #{@prop}"
69
+ elsif @prop.to_s.end_with? '!'
70
+ @prop = @prop.to_s.sub('!', '')
71
+ m = m.updated(m.type, [@prop, *m.children[1..2]])
72
+ else
73
+ @rbstack.last[@prop] = s(:self)
74
+ end
75
+
76
+ begin
77
+ @instance_method = m
78
+ parse m
79
+ ensure
80
+ @instance_method = nil
81
+ end
82
+
83
+ elsif
84
+ [:defs, :asyncs].include? m.type and m.children.first.type == :self
85
+ then
86
+
87
+ @prop = "static #{m.children[1]}"
88
+ if not m.is_method?
89
+ @prop = "static get #{m.children[1]}"
90
+ m = m.updated(m.type, [*m.children[0..2],
91
+ s(:autoreturn, m.children[3])])
92
+ elsif @prop.to_s.end_with? '='
93
+ @prop = "static set #{m.children[1].to_s.sub('=', '')}"
94
+ elsif @prop.to_s.end_with? '!'
95
+ m = m.updated(m.type, [m.children[0],
96
+ m.children[1].to_s.sub('!', ''), *m.children[2..3]])
97
+ @prop = "static #{m.children[1]}"
98
+ end
99
+
100
+ @prop.sub! 'static', 'static async' if m.type == :asyncs
101
+
102
+ m = m.updated(:def, m.children[1..3])
103
+ parse m
104
+
105
+ elsif m.type == :send and m.children.first == nil
106
+ if m.children[1] == :attr_accessor
107
+ m.children[2..-1].each_with_index do |child_sym, index2|
108
+ put @sep unless index2 == 0
109
+ var = child_sym.children.first
110
+ put "get #{var}() {#{@nl}return this._#{var}#@nl}#@sep"
111
+ put "set #{var}(#{var}) {#{@nl}this._#{var} = #{var}#@nl}"
112
+ end
113
+ elsif m.children[1] == :attr_reader
114
+ m.children[2..-1].each_with_index do |child_sym, index2|
115
+ put @sep unless index2 == 0
116
+ var = child_sym.children.first
117
+ put "get #{var}() {#{@nl}return this._#{var}#@nl}"
118
+ end
119
+ elsif m.children[1] == :attr_writer
120
+ m.children[2..-1].each_with_index do |child_sym, index2|
121
+ put @sep unless index2 == 0
122
+ var = child_sym.children.first
123
+ put "set #{var}(#{var}) {#{@nl}this._#{var} = #{var}#@nl}"
124
+ end
125
+ else
126
+ skipped = true
127
+ end
128
+
129
+ else
130
+ skipped = true
131
+
132
+ if m.type == :casgn and m.children[0] == nil
133
+ @rbstack.last[m.children[1]] = name
134
+ elsif m.type == :alias
135
+ @rbstack.last[m.children[0]] = name
136
+ end
137
+ end
138
+
139
+ post << m if skipped
140
+ end
141
+
142
+ put @nl unless skipped
143
+ put '}'
144
+
145
+ post.each do |m|
146
+ put @sep
147
+ if m.type == :alias
148
+ parse name
149
+ put '.prototype.'
150
+ put m.children[0].children[0]
151
+ put ' = '
152
+ parse name
153
+ put '.prototype.'
154
+ put m.children[1].children[0]
155
+ else
156
+ parse m, :statement
157
+ end
158
+ end
159
+
160
+ ensure
161
+ @class_name = class_name
162
+ @class_parent = class_parent
163
+ @rbstack.pop
164
+ end
165
+ end
166
+ end
167
+ end
@@ -6,7 +6,7 @@ module Ruby2JS
6
6
  # (arg :x)
7
7
  # (...)
8
8
 
9
- handle :def do |name, args, body=nil|
9
+ handle :def, :async do |name, args, body=nil|
10
10
  body ||= s(:begin)
11
11
  if name =~ /[!?]$/
12
12
  raise NotImplementedError, "invalid method name #{ name }"
@@ -16,7 +16,7 @@ module Ruby2JS
16
16
  vars.merge! @vars unless name
17
17
  if args and !args.children.empty?
18
18
  # splats
19
- if args.children.last.type == :restarg
19
+ if args.children.last.type == :restarg and not es2015
20
20
  if args.children[-1].children.first
21
21
  body = s(:begin, body) unless body.type == :begin
22
22
  assign = s(:lvasgn, args.children[-1].children.first,
@@ -68,7 +68,7 @@ module Ruby2JS
68
68
 
69
69
  # optional arguments
70
70
  args.children.each_with_index do |arg, i|
71
- if arg.type == :optarg
71
+ if arg.type == :optarg and not es2015
72
72
  body = s(:begin, body) unless body.type == :begin
73
73
  argname, value = arg.children
74
74
  children = args.children.dup
@@ -88,13 +88,50 @@ module Ruby2JS
88
88
  end
89
89
  end
90
90
 
91
+ put 'async ' if @ast.type == :async
92
+
93
+ # es2015 fat arrow support
94
+ if not name and es2015 and @state != :method
95
+ put '('; parse args; put ') => '
96
+
97
+ expr = body
98
+ expr = expr.children.first if expr.type == :autoreturn
99
+ while expr.type == :begin and expr.children.length == 1
100
+ expr = expr.children.first
101
+ end
102
+
103
+ if EXPRESSIONS.include? expr.type
104
+ if expr.type == :send and expr.children[0..1] == [nil, :raise]
105
+ style = :statement
106
+ else
107
+ style = :expression
108
+ end
109
+ elsif
110
+ expr.type == :if and expr.children[1] and expr.children[2] and
111
+ EXPRESSIONS.include? expr.children[1].type and
112
+ EXPRESSIONS.include? expr.children[2].type
113
+ then
114
+ style = :expression
115
+ else
116
+ style = :statement
117
+ end
118
+
119
+ if style == :expression
120
+ expr.type == :hash ? group(expr) : parse(expr)
121
+ else
122
+ put "{#{@nl}"; scope body, vars; put "#{@nl}}"
123
+ end
124
+
125
+ return
126
+ end
127
+
91
128
  nl = @nl unless body == s(:begin)
92
129
  begin
93
- if name
94
- put "function #{name}"
95
- elsif @prop
130
+ if @prop
96
131
  put @prop
97
132
  @prop = nil
133
+ elsif name
134
+ put "function #{name}"
98
135
  else
99
136
  put 'function'
100
137
  end
@@ -116,5 +153,16 @@ module Ruby2JS
116
153
  @block_depth -= 1 if @block_depth
117
154
  end
118
155
  end
156
+
157
+ handle :optarg do |name, value|
158
+ put name
159
+ put '='
160
+ parse value
161
+ end
162
+
163
+ handle :restarg do |name|
164
+ put '...'
165
+ put name
166
+ end
119
167
  end
120
168
  end
@@ -5,10 +5,17 @@ module Ruby2JS
5
5
  # (args)
6
6
  # (...)
7
7
 
8
- # NOTE: defp is only produced by filters
8
+ # NOTE: defp and asyncs are only produced by filters
9
9
 
10
- handle :defs, :defp do |target, method, args, body|
11
- parse transform_defs(target, method, args, body)
10
+ handle :defs, :defp, :asyncs do |target, method, args, body|
11
+ node = transform_defs(target, method, args, body)
12
+
13
+ if node.type == :send and @ast.type == :asyncs
14
+ node = node.updated(nil, [*node.children[0..1],
15
+ node.children[2].updated(:async)])
16
+ end
17
+
18
+ parse node, :method
12
19
  end
13
20
 
14
21
  def transform_defs(target, method, args, body)
@@ -23,8 +30,7 @@ module Ruby2JS
23
30
  set: s(:block, s(:send, nil, :proc), args,
24
31
  body)})
25
32
  else
26
- node = s(:send, target, "#{method}=",
27
- s(:block, s(:send, nil, :proc), args, body))
33
+ node = s(:send, target, "#{method}=", s(:def, nil, args, body))
28
34
  end
29
35
 
30
36
  @comments[node] = @comments[@ast] if @comments[@ast]
@@ -10,6 +10,32 @@ module Ruby2JS
10
10
  # (...))
11
11
 
12
12
  handle :dstr, :dsym do |*children|
13
+ if es2015
14
+ put '`'
15
+
16
+ # gather length of string parts; if long enough, newlines will
17
+ # not be escaped
18
+ length = children.select {|child| child.type==:str}.
19
+ map {|child| child.children.last.length}.inject(:+)
20
+
21
+ children.each do |child|
22
+ if child.type == :str
23
+ str = child.children.first.inspect[1..-2].gsub('${', '$\{')
24
+ if length > 40
25
+ put str.gsub("\\n", "\n")
26
+ else
27
+ put str
28
+ end
29
+ else
30
+ put '${'
31
+ parse child
32
+ put '}'
33
+ end
34
+ end
35
+ put '`'
36
+ return
37
+ end
38
+
13
39
  children.each_with_index do |child, index|
14
40
  put ' + ' unless index == 0
15
41
 
@@ -9,16 +9,16 @@ module Ruby2JS
9
9
  # (int 1))
10
10
  # (...)
11
11
 
12
- handle :for do |var, expression, block|
12
+ handle :for, :for_of do |var, expression, block|
13
13
  begin
14
14
  next_token, @next_token = @next_token, :continue
15
- put "for (var "; parse var
15
+ put "for (#{es2015 ? 'let' : 'var'} "; parse var
16
16
  if [:irange, :erange].include? expression.type
17
17
  put ' = '; parse expression.children.first; put '; '; parse var
18
18
  (expression.type == :erange ? put(' < ') : put(' <= '))
19
19
  parse expression.children.last; put '; '; parse var; put '++'
20
20
  else
21
- put ' in '; parse expression;
21
+ put (@ast.type==:for_of ? ' of ' : ' in '); parse expression;
22
22
  end
23
23
  puts ') {'; scope block; sput '}'
24
24
  ensure