ruby2js 2.1.24 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -56,8 +56,8 @@ module Ruby2JS
56
56
  # hoist get/set comments to definition of property
57
57
  if right.type == :hash
58
58
  right.children.each do |pair|
59
- next unless Parser::AST::Node === pair.children.last
60
- if pair.children.last.type == :block
59
+ next unless Parser::AST::Node === pair.children.last
60
+ if [:block, :def, :async].include? pair.children.last.type
61
61
  if @comments[pair.children.last]
62
62
  (puts ''; singleton = false) if singleton
63
63
  comments(pair.children.last).each do |comment|
@@ -68,15 +68,40 @@ module Ruby2JS
68
68
  end
69
69
  end
70
70
 
71
+ # check to see if es2015 anonymous function syntax can be used
72
+ anonfn = (es2015 and right and right.type == :block)
73
+ if anonfn
74
+ receiver, method = right.children[0].children
75
+ if receiver
76
+ unless method == :new and receiver.children == [nil, :Proc]
77
+ anonfn = false
78
+ end
79
+ elsif not [:lambda, :proc].include? method
80
+ anonfn = false
81
+ end
82
+ end
83
+
71
84
  if
85
+ anonfn and
72
86
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
73
87
  then
74
- put left.children.first
88
+ @prop = left.children.first
89
+ parse right, :method
75
90
  else
76
- parse left
91
+ if not [:str, :sym].include? left.type and es2015
92
+ put '['
93
+ parse left
94
+ put ']'
95
+ elsif
96
+ left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
97
+ then
98
+ put left.children.first
99
+ else
100
+ parse left
101
+ end
102
+
103
+ put ': '; parse right
77
104
  end
78
-
79
- put ': '; parse right
80
105
  end
81
106
 
82
107
  ensure
@@ -50,7 +50,7 @@ module Ruby2JS
50
50
  if scope
51
51
  vars = @vars.select {|key, value| value == :pending}.keys
52
52
  unless vars.empty?
53
- insert mark, "var #{vars.join(', ')}#{@sep}"
53
+ insert mark, "#{es2015 ? 'let' : 'var'} #{vars.join(', ')}#{@sep}"
54
54
  vars.each {|var| @vars[var] = true}
55
55
  end
56
56
  end
@@ -4,8 +4,11 @@ module Ruby2JS
4
4
  # (ivasgn :@a
5
5
  # (int 1))
6
6
 
7
- handle :ivasgn do |var, expression|
8
- put "#{ var.to_s.sub('@', 'this._') } = "; parse expression
7
+ handle :ivasgn do |var, expression=nil|
8
+ put "#{ var.to_s.sub('@', 'this._') }"
9
+ if expression
10
+ put " = "; parse expression
11
+ end
9
12
  end
10
13
  end
11
14
  end
@@ -10,11 +10,48 @@ module Ruby2JS
10
10
  # (int 2)))
11
11
 
12
12
  handle :masgn do |lhs, rhs|
13
- block = []
14
- lhs.children.zip rhs.children.zip do |var, val|
15
- block << s(var.type, *var.children, *val)
13
+ if es2015
14
+ walk = lambda do |node|
15
+ results = []
16
+ node.children.each do |var|
17
+ if var.type == :lvasgn
18
+ results << var
19
+ elsif var.type == :mlhs or var.type == :splat
20
+ results += walk[var]
21
+ end
22
+ end
23
+ results
24
+ end
25
+
26
+ vars = walk[lhs]
27
+ newvars = vars.select {|var| not @vars.include? var.children[0]}
28
+
29
+ if newvars.length > 0
30
+ if vars == newvars
31
+ put 'let '
32
+ else
33
+ put "let #{newvars.map {|var| var.children.last}.join(', ')}#{@sep}"
34
+ end
35
+ end
36
+
37
+ newvars.each do |var|
38
+ @vars[var.children.last] ||= (@scope ? true : :pending)
39
+ end
40
+
41
+ put '['
42
+ lhs.children.each_with_index do |child, index|
43
+ put ", " unless index == 0
44
+ parse child
45
+ end
46
+ put "] = "
47
+ parse rhs
48
+ else
49
+ block = []
50
+ lhs.children.zip rhs.children.zip do |var, val|
51
+ block << s(var.type, *var.children, *val)
52
+ end
53
+ parse s(:begin, *block), @state
16
54
  end
17
- parse s(:begin, *block), @state
18
55
  end
19
56
  end
20
57
  end
@@ -9,11 +9,11 @@ module Ruby2JS
9
9
  # (sendw nil :puts
10
10
  # (int 1))
11
11
 
12
- # Note: attr and sendw are only generated by filters. Attr forces
12
+ # Note: attr, sendw, and await are only generated by filters. Attr forces
13
13
  # interpretation as an attribute vs a function call with zero parameters.
14
14
  # Sendw forces parameters to be placed on separate lines.
15
15
 
16
- handle :send, :sendw, :attr, :call do |receiver, method, *args|
16
+ handle :send, :sendw, :await, :attr, :call do |receiver, method, *args|
17
17
  ast = @ast
18
18
 
19
19
  # strip '!' and '?' decorations
@@ -21,13 +21,13 @@ module Ruby2JS
21
21
 
22
22
  # three ways to define anonymous functions
23
23
  if method == :new and receiver and receiver.children == [nil, :Proc]
24
- return parse args.first
24
+ return parse args.first, @state
25
25
  elsif not receiver and [:lambda, :proc].include? method
26
26
  if method == :lambda
27
27
  return parse s(args.first.type, *args.first.children[0..-2],
28
- s(:autoreturn, args.first.children[-1]))
28
+ s(:autoreturn, args.first.children[-1])), @state
29
29
  else
30
- return parse args.first
30
+ return parse args.first, @state
31
31
  end
32
32
  end
33
33
 
@@ -35,12 +35,59 @@ module Ruby2JS
35
35
  if [:call, :[]].include? method and receiver and receiver.type == :block
36
36
  t2,m2,*args2 = receiver.children.first.children
37
37
  if not t2 and [:lambda, :proc].include? m2 and args2.length == 0
38
- (@state == :statement ? group(receiver) : parse(receiver))
38
+ (es2015 || @state == :statement ? group(receiver) : parse(receiver))
39
39
  put '('; parse_all(*args, join: ', '); put ')'
40
40
  return
41
41
  end
42
42
  end
43
43
 
44
+ # async/await support
45
+ if es2017 and receiver == nil and args.length == 1
46
+ if method == :async
47
+ if args.first.type == :def
48
+ # async def f(x) {...}
49
+ return parse args.first.updated :async
50
+
51
+ elsif args.first.type == :defs
52
+ # async def o.m(x) {...}
53
+ return parse args.first.updated :asyncs
54
+
55
+ elsif args.first.type == :block
56
+ block = args.first
57
+
58
+ if block.children[0].children.last == :lambda
59
+ # async lambda {|x| ... }
60
+ # async -> (x) { ... }
61
+ return parse block.updated(:async, [nil, block.children[1],
62
+ s(:autoreturn, block.children[2])])
63
+
64
+ elsif block.children[0].children.last == :proc
65
+ # async proc {|x| ... }
66
+ return parse block.updated(:async, [nil, *block.children[1..-1]])
67
+
68
+ elsif
69
+ block.children[0].children[1] == :new and
70
+ block.children[0].children[0] == s(:const, nil, :Proc)
71
+ then
72
+ # async Proc.new {|x| ... }
73
+ return parse block.updated(:async, [nil, *block.children[1..-1]])
74
+ end
75
+ end
76
+
77
+ elsif method == :await
78
+ if args.first.type == :send
79
+ # await f(x)
80
+ return parse args.first.updated(:await)
81
+
82
+ elsif args.first.type == :block
83
+ # await f(x) { ... }
84
+ block = args.first
85
+ return parse block.updated nil, [block.children[0].updated(:await),
86
+ *block.children[1..-1]]
87
+ end
88
+ end
89
+ end
90
+
44
91
  op_index = operator_index method
45
92
  if op_index != -1
46
93
  target = args.first
@@ -76,6 +123,13 @@ module Ruby2JS
76
123
  parse receiver; put '['; parse_all(*args[0..-2], join: ', '); put '] = '
77
124
  parse args[-1]
78
125
 
126
+ elsif method == :** and not es2016
127
+ put 'Math.pow('
128
+ parse receiver
129
+ put ', '
130
+ parse args.first
131
+ put ')'
132
+
79
133
  elsif [:-@, :+@, :~, '~'].include? method
80
134
  put method.to_s[0]; parse receiver
81
135
 
@@ -99,7 +153,7 @@ module Ruby2JS
99
153
  elsif method =~ /=$/
100
154
  parse receiver
101
155
  put "#{ '.' if receiver }#{ method.to_s.sub(/=$/, ' =') } "
102
- parse args.first
156
+ parse args.first, (@state == :method ? :method : :expression)
103
157
 
104
158
  elsif method == :new
105
159
  if receiver
@@ -166,6 +220,8 @@ module Ruby2JS
166
220
  put 'typeof '; parse args.first
167
221
 
168
222
  else
223
+ put 'await ' if @ast.type == :await
224
+
169
225
  if not ast.is_method?
170
226
  if receiver
171
227
  (group_receiver ? group(receiver) : parse(receiver))
@@ -173,7 +229,7 @@ module Ruby2JS
173
229
  else
174
230
  parse ast.updated(:lvasgn, [method]), @state
175
231
  end
176
- elsif args.length > 0 and args.any? {|arg| arg.type == :splat}
232
+ elsif args.any? {|arg| arg.type == :splat} and not es2015
177
233
  parse s(:send, s(:attr, receiver, method), :apply,
178
234
  (receiver || s(:nil)), s(:array, *args))
179
235
  else
@@ -206,11 +262,16 @@ module Ruby2JS
206
262
  # build up chain of conditional evaluations
207
263
  until stack.empty?
208
264
  node = stack.pop
209
- expr = node.updated(:send, [expr, *node.children[1..-1]])
210
- result = s(:and, result, expr)
265
+ expr = node.updated(:send, [expr, *node.children[1..-1]])
266
+ result = s(:and, result, expr)
211
267
  end
212
268
 
213
269
  parse result
214
270
  end
271
+
272
+ handle :splat do |expr|
273
+ put '...'
274
+ parse expr
275
+ end
215
276
  end
216
277
  end
@@ -6,7 +6,7 @@ module Ruby2JS
6
6
  # (super ...)
7
7
 
8
8
  handle :super, :zsuper do |*args|
9
- unless @class_parent and @instance_method
9
+ unless @instance_method and @class_parent
10
10
  raise NotImplementedError, "super outside of a method"
11
11
  end
12
12
 
@@ -21,15 +21,28 @@ module Ruby2JS
21
21
  end
22
22
  end
23
23
 
24
- parse @class_parent
24
+ if es2015
25
+ if @instance_method.children[0] == :constructor
26
+ put 'super'
27
+ else
28
+ put 'super.'
29
+ put @instance_method.children[0]
30
+ end
25
31
 
26
- # what to call
27
- if @instance_method.type != :constructor
28
- puts ".prototype.#{ @instance_method.children[1].to_s.chomp('=') }"
29
- end
32
+ put '('
33
+ parse s(:args, *args)
34
+ put ')'
35
+ else
36
+ parse @class_parent
30
37
 
31
- if args
32
- put '.call('; parse_all s(:self), *args, join: ', '; put ')'
38
+ # what to call
39
+ if @instance_method.type != :constructor
40
+ puts ".prototype.#{ @instance_method.children[1].to_s.chomp('=') }"
41
+ end
42
+
43
+ if args
44
+ put '.call('; parse_all s(:self), *args, join: ', '; put ')'
45
+ end
33
46
  end
34
47
  end
35
48
  end
@@ -27,7 +27,11 @@ module Ruby2JS
27
27
  end
28
28
 
29
29
  if state == :statement and @scope and not @vars.include?(name)
30
- var = 'var '
30
+ if es2015
31
+ var = 'let '
32
+ else
33
+ var = 'var '
34
+ end
31
35
  end
32
36
 
33
37
  if value
@@ -9,6 +9,19 @@ module Ruby2JS
9
9
  begin
10
10
  next_token, @next_token = @next_token, :continue
11
11
 
12
+ # handle while loops that assign a variable
13
+ while condition.type == :begin and condition.children.length == 1
14
+ condition = condition.children.first
15
+ end
16
+
17
+ if condition.type == :lvasgn
18
+ var = condition.children[0]
19
+ unless @vars[var]
20
+ put "#{es2015 ? 'let' : 'var'} #{var}#@sep"
21
+ @vars[var] = true
22
+ end
23
+ end
24
+
12
25
  put 'while ('; parse condition; puts ') {'; scope block; sput '}'
13
26
  ensure
14
27
  @next_token = next_token
@@ -5,8 +5,9 @@ module Ruby2JS
5
5
  attr_accessor :ast
6
6
 
7
7
  LOGICAL = :and, :not, :or
8
- OPERATORS = [:[], :[]=], [:not, :!], [:*, :/, :%], [:+, :-], [:>>, :<<],
9
- [:&], [:^, :|], [:<=, :<, :>, :>=], [:==, :!=, :===, :"!=="], [:and, :or]
8
+ OPERATORS = [:[], :[]=], [:not, :!], [:**], [:*, :/, :%], [:+, :-],
9
+ [:>>, :<<], [:&], [:^, :|], [:<=, :<, :>, :>=], [:==, :!=, :===, :"!=="],
10
+ [:and, :or]
10
11
 
11
12
  INVERT_OP = {
12
13
  :< => :>=,
@@ -44,6 +45,9 @@ module Ruby2JS
44
45
  @prototype = nil
45
46
  @class_parent = nil
46
47
  @class_name = nil
48
+
49
+ @eslevel = :es5
50
+ @strict = false
47
51
  end
48
52
 
49
53
  def width=(width)
@@ -52,6 +56,14 @@ module Ruby2JS
52
56
 
53
57
  def convert
54
58
  parse( @ast, :statement )
59
+
60
+ if @strict
61
+ if @sep == '; '
62
+ @lines.first.unshift "\"use strict\"#@sep"
63
+ else
64
+ @lines.unshift Line.new('"use strict";')
65
+ end
66
+ end
55
67
  end
56
68
 
57
69
  def operator_index op
@@ -73,6 +85,20 @@ module Ruby2JS
73
85
  Parser::AST::Node.new(type, args)
74
86
  end
75
87
 
88
+ attr_accessor :strict, :eslevel
89
+
90
+ def es2015
91
+ @eslevel >= 2015
92
+ end
93
+
94
+ def es2016
95
+ @eslevel >= 2016
96
+ end
97
+
98
+ def es2017
99
+ @eslevel >= 2017
100
+ end
101
+
76
102
  @@handlers = []
77
103
  def self.handle(*types, &block)
78
104
  types.each do |type|
@@ -132,9 +158,9 @@ module Ruby2JS
132
158
  end
133
159
 
134
160
  def parse_all(*args)
135
- options = (Hash === args.last) ? args.pop : {}
136
- sep = options[:join].to_s
137
- state = options[:state] || :expression
161
+ @options = (Hash === args.last) ? args.pop : {}
162
+ sep = @options[:join].to_s
163
+ state = @options[:state] || :expression
138
164
 
139
165
  args.each_with_index do |arg, index|
140
166
  put sep unless index == 0
@@ -211,6 +237,7 @@ require 'ruby2js/converter/break'
211
237
  require 'ruby2js/converter/case'
212
238
  require 'ruby2js/converter/casgn'
213
239
  require 'ruby2js/converter/class'
240
+ require 'ruby2js/converter/class2'
214
241
  require 'ruby2js/converter/const'
215
242
  require 'ruby2js/converter/cvar'
216
243
  require 'ruby2js/converter/cvasgn'
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2015'
2
+
3
+ Ruby2JS.strict_default = true
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2015
4
+ Ruby2JS.eslevel_default = 2015
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2016'
2
+
3
+ Ruby2JS.strict_default = true
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2016
4
+ Ruby2JS.eslevel_default = 2016
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2017'
2
+
3
+ Ruby2JS.strict_default = true
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2017
4
+ Ruby2JS.eslevel_default = 2017
5
+ end
@@ -7,7 +7,7 @@ module Ruby2JS
7
7
  end
8
8
 
9
9
  def self.eval(source, options={})
10
- ExecJS.eval(convert(source, options).to_s)
10
+ ExecJS.eval(convert(source, options.merge(strict: false)).to_s)
11
11
  end
12
12
 
13
13
  def self.exec(source, options={})
@@ -17,7 +17,7 @@ module Ruby2JS
17
17
  if node.children[0] == nil and WHITELIST.include? node.children[1].to_s
18
18
  super
19
19
  elsif node.children[1] =~ /_.*\w$/
20
- super S((node.is_method? ? :call : :attr) , node.children[0],
20
+ super S(:send , node.children[0],
21
21
  camelCase(node.children[1]), *node.children[2..-1])
22
22
  else
23
23
  super
@@ -47,8 +47,12 @@ module Ruby2JS
47
47
  process S(:call, target, :toString, *args)
48
48
 
49
49
  elsif method == :Array and target == nil
50
- process S(:send, s(:attr, s(:attr, s(:const, nil, :Array),
51
- :prototype), :slice), :call, *args)
50
+ if es2015
51
+ process S(:send, s(:const, nil, :Array), :from, *args)
52
+ else
53
+ process S(:send, s(:attr, s(:attr, s(:const, nil, :Array),
54
+ :prototype), :slice), :call, *args)
55
+ end
52
56
 
53
57
  elsif method == :to_i
54
58
  process node.updated :send, [nil, :parseInt, target, *args]
@@ -368,6 +372,49 @@ module Ruby2JS
368
372
  :step, step])
369
373
  process node.updated(nil, [call, *node.children[1..-1]])
370
374
 
375
+ elsif
376
+ # (a..b).each {|v| ...}
377
+ call.children[1] == :each and
378
+ call.children[0].type == :begin and
379
+ call.children[0].children.length == 1 and
380
+ [:irange, :erange].include? call.children[0].children[0].type and
381
+ node.children[1].children.length == 1
382
+ then
383
+ s(:for, s(:lvasgn, node.children[1].children[0].children[0]),
384
+ call.children[0].children[0], node.children[2])
385
+
386
+ elsif
387
+ [:each, :each_value].include? call.children[1] and
388
+ node.children[1].children.length == 1
389
+ then
390
+ if es2015
391
+ process node.updated(:for_of,
392
+ [s(:lvasgn, node.children[1].children[0].children[0]),
393
+ node.children[0].children[0], node.children[2]])
394
+ else
395
+ process node.updated(nil, [s(:send, call.children[0],
396
+ :forEach), *node.children[1..2]])
397
+ end
398
+
399
+ elsif
400
+ call.children[1] == :each_key and
401
+ [:each, :each_key].include? call.children[1] and
402
+ node.children[1].children.length == 1
403
+ then
404
+ process node.updated(:for,
405
+ [s(:lvasgn, node.children[1].children[0].children[0]),
406
+ node.children[0].children[0], node.children[2]])
407
+
408
+ elsif es2015 and call.children[1] == :inject
409
+ process node.updated(:send, [call.children[0], :reduce,
410
+ s(:block, s(:send, nil, :lambda), *node.children[1..2]),
411
+ *call.children[2..-1]])
412
+
413
+ elsif es2017 and call.children[1] == :each_pair
414
+ process node.updated(nil, [s(:send, s(:send, s(:const, nil, :Object),
415
+ :entries, call.children[0]), :forEach), s(:args, s(:mlhs,
416
+ *node.children[1].children)), node.children[2]])
417
+
371
418
  else
372
419
  super
373
420
  end