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
@@ -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