ruby2js 3.0.15 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -7
  3. data/lib/ruby2js.rb +30 -3
  4. data/lib/ruby2js/converter.rb +23 -3
  5. data/lib/ruby2js/converter/args.rb +31 -1
  6. data/lib/ruby2js/converter/block.rb +2 -2
  7. data/lib/ruby2js/converter/casgn.rb +3 -2
  8. data/lib/ruby2js/converter/class.rb +2 -2
  9. data/lib/ruby2js/converter/class2.rb +117 -13
  10. data/lib/ruby2js/converter/cvar.rb +5 -3
  11. data/lib/ruby2js/converter/cvasgn.rb +5 -3
  12. data/lib/ruby2js/converter/def.rb +2 -2
  13. data/lib/ruby2js/converter/for.rb +8 -1
  14. data/lib/ruby2js/converter/hash.rb +22 -6
  15. data/lib/ruby2js/converter/if.rb +13 -2
  16. data/lib/ruby2js/converter/import.rb +38 -0
  17. data/lib/ruby2js/converter/ivar.rb +2 -0
  18. data/lib/ruby2js/converter/ivasgn.rb +1 -1
  19. data/lib/ruby2js/converter/kwbegin.rb +16 -2
  20. data/lib/ruby2js/converter/logical.rb +46 -1
  21. data/lib/ruby2js/converter/opasgn.rb +5 -2
  22. data/lib/ruby2js/converter/regexp.rb +27 -2
  23. data/lib/ruby2js/converter/return.rb +1 -1
  24. data/lib/ruby2js/converter/send.rb +160 -69
  25. data/lib/ruby2js/converter/super.rb +15 -9
  26. data/lib/ruby2js/converter/xnode.rb +89 -0
  27. data/lib/ruby2js/es2018.rb +5 -0
  28. data/lib/ruby2js/es2018/strict.rb +3 -0
  29. data/lib/ruby2js/es2019.rb +5 -0
  30. data/lib/ruby2js/es2019/strict.rb +3 -0
  31. data/lib/ruby2js/es2020.rb +5 -0
  32. data/lib/ruby2js/es2020/strict.rb +3 -0
  33. data/lib/ruby2js/es2021.rb +5 -0
  34. data/lib/ruby2js/es2021/strict.rb +3 -0
  35. data/lib/ruby2js/filter.rb +1 -0
  36. data/lib/ruby2js/filter/cjs.rb +2 -2
  37. data/lib/ruby2js/filter/esm.rb +72 -0
  38. data/lib/ruby2js/filter/functions.rb +218 -34
  39. data/lib/ruby2js/filter/matchAll.rb +49 -0
  40. data/lib/ruby2js/filter/node.rb +18 -10
  41. data/lib/ruby2js/filter/nokogiri.rb +13 -13
  42. data/lib/ruby2js/filter/react.rb +190 -30
  43. data/lib/ruby2js/filter/require.rb +1 -4
  44. data/lib/ruby2js/filter/rubyjs.rb +4 -4
  45. data/lib/ruby2js/filter/vue.rb +45 -17
  46. data/lib/ruby2js/filter/wunderbar.rb +63 -0
  47. data/lib/ruby2js/serializer.rb +25 -11
  48. data/lib/ruby2js/version.rb +2 -2
  49. metadata +16 -4
@@ -33,21 +33,99 @@ module Ruby2JS
33
33
  class_name, @class_name = @class_name, name
34
34
  class_parent, @class_parent = @class_parent, inheritance
35
35
  @rbstack.push({})
36
+ constructor = []
37
+ index = 0
36
38
 
37
- # capture method names for automatic self referencing
38
- body.each_with_index do |m, index|
39
+ # capture constructor, method names for automatic self referencing
40
+ body.each do |m|
39
41
  if m.type == :def
40
42
  prop = m.children.first
41
- unless prop == :initialize or prop.to_s.end_with? '='
43
+ if prop == :initialize
44
+ constructor = m.children[2..-1]
45
+ elsif not prop.to_s.end_with? '='
42
46
  @rbstack.last[prop] = s(:self)
43
47
  end
44
48
  end
45
49
  end
46
50
 
51
+ # private variable declarations
52
+ if es2020
53
+ ivars = Set.new
54
+ cvars = Set.new
55
+
56
+ # find ivars and cvars
57
+ walk = proc do |ast|
58
+ ivars << ast.children.first if ast.type === :ivar
59
+ ivars << ast.children.first if ast.type === :ivasgn
60
+ cvars << ast.children.first if ast.type === :cvar
61
+ cvars << ast.children.first if ast.type === :cvasgn
62
+
63
+ ast.children.each do |child|
64
+ walk[child] if child.is_a? Parser::AST::Node
65
+ end
66
+
67
+ if ast.type == :send and ast.children.first == nil
68
+ if ast.children[1] == :attr_accessor
69
+ ast.children[2..-1].each_with_index do |child_sym, index2|
70
+ ivars << :"@#{child_sym.children.first}"
71
+ end
72
+ elsif ast.children[1] == :attr_reader
73
+ ast.children[2..-1].each_with_index do |child_sym, index2|
74
+ ivars << :"@#{child_sym.children.first}"
75
+ end
76
+ elsif ast.children[1] == :attr_writer
77
+ ast.children[2..-1].each_with_index do |child_sym, index2|
78
+ ivars << :"@#{child_sym.children.first}"
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ walk[@ast]
85
+
86
+ # process leading initializers in constructor
87
+ while constructor.length == 1 and constructor.first.type == :begin
88
+ constructor = constructor.first.children.dup
89
+ end
90
+
91
+ # emit additional class declarations
92
+ unless cvars.empty?
93
+ body.each do |m|
94
+ cvars.delete m.children.first if m.type == :cvasgn
95
+ end
96
+ end
97
+ cvars.to_a.sort.each do |cvar|
98
+ put(index == 0 ? @nl : @sep)
99
+ index += 1
100
+ put 'static #$' + cvar.to_s[2..-1]
101
+ end
102
+
103
+ while constructor.length > 0 and constructor.first.type == :ivasgn
104
+ put(index == 0 ? @nl : @sep)
105
+ index += 1
106
+ statement = constructor.shift
107
+ put '#'
108
+ put statement.children.first.to_s[1..-1]
109
+ put ' = '
110
+ parse statement.children.last
111
+
112
+ ivars.delete statement.children.first
113
+ end
114
+
115
+ # emit additional instance declarations
116
+ ivars.to_a.sort.each do |ivar|
117
+ put(index == 0 ? @nl : @sep)
118
+ index += 1
119
+ put '#' + ivar.to_s[1..-1]
120
+ end
121
+ end
122
+
123
+ # process class definition
47
124
  post = []
48
125
  skipped = false
49
- body.each_with_index do |m, index|
126
+ body.each do |m|
50
127
  put(index == 0 ? @nl : @sep) unless skipped
128
+ index += 1
51
129
  comments = comments(m)
52
130
  location = output_location
53
131
  skipped = false
@@ -67,7 +145,13 @@ module Ruby2JS
67
145
 
68
146
  if @prop == :initialize
69
147
  @prop = :constructor
70
- m = m.updated(m.type, [@prop, *m.children[1..2]])
148
+
149
+ if constructor == [] or constructor == [(:super)]
150
+ skipped = true
151
+ next
152
+ end
153
+
154
+ m = m.updated(m.type, [@prop, m.children[1], s(:begin, *constructor)])
71
155
  elsif not m.is_method?
72
156
  @prop = "get #{@prop}"
73
157
  m = m.updated(m.type, [*m.children[0..1],
@@ -83,12 +167,13 @@ module Ruby2JS
83
167
 
84
168
  begin
85
169
  @instance_method = m
86
- parse m
170
+ @class_method = nil
171
+ parse m # unless skipped
87
172
  ensure
88
173
  @instance_method = nil
89
174
  end
90
175
 
91
- elsif
176
+ elsif \
92
177
  [:defs, :asyncs].include? m.type and m.children.first.type == :self
93
178
  then
94
179
 
@@ -108,27 +193,35 @@ module Ruby2JS
108
193
  @prop.sub! 'static', 'static async' if m.type == :asyncs
109
194
 
110
195
  m = m.updated(:def, m.children[1..3])
111
- parse m
196
+ begin
197
+ @instance_method = nil
198
+ @class_method = m
199
+ parse m # unless skipped
200
+ ensure
201
+ @instance_method = nil
202
+ end
112
203
 
113
204
  elsif m.type == :send and m.children.first == nil
205
+ p = es2020 ? '#' : '_'
206
+
114
207
  if m.children[1] == :attr_accessor
115
208
  m.children[2..-1].each_with_index do |child_sym, index2|
116
209
  put @sep unless index2 == 0
117
210
  var = child_sym.children.first
118
- put "get #{var}() {#{@nl}return this._#{var}#@nl}#@sep"
119
- put "set #{var}(#{var}) {#{@nl}this._#{var} = #{var}#@nl}"
211
+ put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}#@sep"
212
+ put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
120
213
  end
121
214
  elsif m.children[1] == :attr_reader
122
215
  m.children[2..-1].each_with_index do |child_sym, index2|
123
216
  put @sep unless index2 == 0
124
217
  var = child_sym.children.first
125
- put "get #{var}() {#{@nl}return this._#{var}#@nl}"
218
+ put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}"
126
219
  end
127
220
  elsif m.children[1] == :attr_writer
128
221
  m.children[2..-1].each_with_index do |child_sym, index2|
129
222
  put @sep unless index2 == 0
130
223
  var = child_sym.children.first
131
- put "set #{var}(#{var}) {#{@nl}this._#{var} = #{var}#@nl}"
224
+ put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
132
225
  end
133
226
  elsif [:private, :protected, :public].include? m.children[1]
134
227
  raise Error.new("class #{m.children[1]} is not supported", @ast)
@@ -143,10 +236,21 @@ module Ruby2JS
143
236
  end
144
237
 
145
238
  else
146
- skipped = true
239
+ if m.type == :cvasgn and es2020
240
+ put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
241
+ parse m.children[1]
242
+ else
243
+ skipped = true
244
+ end
147
245
 
148
246
  if m.type == :casgn and m.children[0] == nil
149
247
  @rbstack.last[m.children[1]] = name
248
+
249
+ if es2020
250
+ put 'static '; put m.children[1].to_s; put ' = '
251
+ parse m.children[2]
252
+ skipped = false
253
+ end
150
254
  elsif m.type == :alias
151
255
  @rbstack.last[m.children[0]] = name
152
256
  end
@@ -4,14 +4,16 @@ module Ruby2JS
4
4
  # (cvar :@@a)
5
5
 
6
6
  handle :cvar do |var|
7
+ prefix = es2020 ? '#$' : '_'
8
+
7
9
  @class_name ||= nil
8
10
  if @class_name
9
11
  parse @class_name
10
- put var.to_s.sub('@@', "._")
12
+ put var.to_s.sub('@@', ".#{prefix}")
11
13
  elsif @prototype
12
- put var.to_s.sub('@@', 'this._')
14
+ put var.to_s.sub('@@', "this.#{prefix}")
13
15
  else
14
- put var.to_s.sub('@@', 'this.constructor._')
16
+ put var.to_s.sub('@@', "this.constructor.#{prefix}")
15
17
  end
16
18
  end
17
19
  end
@@ -7,13 +7,15 @@ module Ruby2JS
7
7
  handle :cvasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
+ prefix = es2020 ? '#$' : '_'
11
+
10
12
  if @class_name
11
13
  parse @class_name
12
- put var.to_s.sub('@@', "._")
14
+ put var.to_s.sub('@@', ".#{prefix}")
13
15
  elsif @prototype
14
- put var.to_s.sub('@@', 'this._')
16
+ put var.to_s.sub('@@', "this.#{prefix}")
15
17
  else
16
- put var.to_s.sub('@@', 'this.constructor._')
18
+ put var.to_s.sub('@@', "this.constructor.#{prefix}")
17
19
  end
18
20
 
19
21
  if expression
@@ -88,7 +88,7 @@ module Ruby2JS
88
88
  put 'async ' if @ast.type == :async
89
89
 
90
90
  # es2015 fat arrow support
91
- if
91
+ if \
92
92
  not name and es2015 and @state != :method and @ast.type != :defm and
93
93
  not @prop
94
94
  then
@@ -105,7 +105,7 @@ module Ruby2JS
105
105
  else
106
106
  style = :expression
107
107
  end
108
- elsif
108
+ elsif \
109
109
  expr.type == :if and expr.children[1] and expr.children[2] and
110
110
  EXPRESSIONS.include? expr.children[1].type and
111
111
  EXPRESSIONS.include? expr.children[2].type
@@ -10,11 +10,18 @@ module Ruby2JS
10
10
  # (...)
11
11
 
12
12
  handle :for, :for_of do |var, expression, block|
13
+ if @jsx and @ast.type == :for_of
14
+ parse s(:block, s(:send, expression, :map),
15
+ s(:args, s(:arg, var.children[0])),
16
+ s(:autoreturn, block))
17
+ return
18
+ end
19
+
13
20
  begin
14
21
  vars = @vars.dup
15
22
  next_token, @next_token = @next_token, :continue
16
23
  put "for (#{es2015 ? 'let' : 'var'} "; parse var
17
- if [:irange, :erange].include? expression.type
24
+ if expression and [:irange, :erange].include? expression.type
18
25
  put ' = '; parse expression.children.first; put '; '; parse var
19
26
  (expression.type == :erange ? put(' < ') : put(' <= '))
20
27
  parse expression.children.last; put '; '; parse var; put '++'
@@ -12,10 +12,26 @@ module Ruby2JS
12
12
 
13
13
  (singleton ? put('{') : puts('{'))
14
14
 
15
- pairs.each_with_index do |node, index|
16
- raise Error.new("kwsplat", @ast) if node.type == :kwsplat
17
-
15
+ index = 0
16
+ while pairs.length > 0
17
+ node = pairs.shift
18
18
  (singleton ? put(', ') : put(",#@ws")) unless index == 0
19
+ index += 1
20
+
21
+ if node.type == :kwsplat
22
+ if es2018
23
+ if node.children.first.type == :hash
24
+ pairs.unshift(*node.children.first.children)
25
+ index = 0
26
+ else
27
+ puts '...'; parse node.children.first
28
+ end
29
+
30
+ next
31
+ else
32
+ raise Error.new("kwsplat", @ast)
33
+ end
34
+ end
19
35
 
20
36
  if not @comments[node].empty?
21
37
  (puts ''; singleton = false) if singleton
@@ -81,13 +97,13 @@ module Ruby2JS
81
97
  end
82
98
  end
83
99
 
84
- if
100
+ if \
85
101
  anonfn and
86
102
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
87
103
  then
88
104
  @prop = left.children.first
89
105
  parse right, :method
90
- elsif
106
+ elsif \
91
107
  es2015 and left.type == :sym and right.type == :lvar and
92
108
  left.children == right.children
93
109
  then
@@ -97,7 +113,7 @@ module Ruby2JS
97
113
  put '['
98
114
  parse left
99
115
  put ']'
100
- elsif
116
+ elsif \
101
117
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
102
118
  then
103
119
  put left.children.first
@@ -55,8 +55,19 @@ module Ruby2JS
55
55
  end
56
56
  else
57
57
  else_block ||= s(:nil)
58
- put '('; parse condition; put ' ? '; parse then_block
59
- put ' : '; parse else_block; put ')'
58
+
59
+ if @jsx
60
+ if then_block.type == :begin
61
+ then_block = s(:xnode, '', *then_block.children)
62
+ end
63
+
64
+ if else_block.type == :begin
65
+ else_block = s(:xnode, '', *else_block.children)
66
+ end
67
+ end
68
+
69
+ parse condition; put ' ? '; parse then_block
70
+ put ' : '; parse else_block
60
71
  end
61
72
  end
62
73
  end
@@ -0,0 +1,38 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (import str const)
5
+
6
+ # NOTE: import is a synthetic
7
+
8
+ handle :import do |path, *args|
9
+ put 'import '
10
+
11
+ args.each_with_index do |arg, index|
12
+ put ', ' unless index == 0
13
+ parse arg
14
+ end
15
+
16
+ put ' from '
17
+ put path.inspect
18
+ end
19
+
20
+ # (export const)
21
+
22
+ # NOTE: export is a synthetic
23
+
24
+ handle :export do |*args|
25
+ put 'export '
26
+
27
+ if args.first == :default
28
+ put 'default '
29
+ args.shift
30
+ end
31
+
32
+ args.each_with_index do |arg, index|
33
+ put ', ' unless index == 0
34
+ parse arg
35
+ end
36
+ end
37
+ end
38
+ end
@@ -6,6 +6,8 @@ module Ruby2JS
6
6
  handle :ivar do |var|
7
7
  if self.ivars and self.ivars.include? var
8
8
  parse s(:hostvalue, self.ivars[var])
9
+ elsif es2020
10
+ parse s(:attr, s(:self), var.to_s.sub('@', '#'))
9
11
  else
10
12
  parse s(:attr, s(:self), var.to_s.sub('@', '_'))
11
13
  end
@@ -7,7 +7,7 @@ module Ruby2JS
7
7
  handle :ivasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
- put "#{ var.to_s.sub('@', 'this._') }"
10
+ put "#{ var.to_s.sub('@', 'this.' + (es2020 ? '#' : '_')) }"
11
11
  if expression
12
12
  put " = "; parse expression
13
13
  end
@@ -51,13 +51,27 @@ module Ruby2JS
51
51
  puts "try {"; scope body; sput '}'
52
52
 
53
53
  if recovers
54
- var ||= s(:gvar, :$EXCEPTION)
55
54
 
56
55
  if recovers.length == 1 and not recovers.first.children.first
56
+ # find reference to exception ($!)
57
+ walk = proc do |ast|
58
+ result = ast if ast.type === :gvar and ast.children.first == :$!
59
+ ast.children.each do |child|
60
+ result ||= walk[child] if child.is_a? Parser::AST::Node
61
+ end
62
+ result
63
+ end
64
+
57
65
  # single catch with no exception named
58
- put " catch ("; parse var; puts ") {"
66
+ if es2019 and not var and not walk[@ast]
67
+ puts " catch {"
68
+ else
69
+ var ||= s(:gvar, :$EXCEPTION)
70
+ put " catch ("; parse var; puts ") {"
71
+ end
59
72
  scope recovers.first.children.last; sput '}'
60
73
  else
74
+ var ||= s(:gvar, :$EXCEPTION)
61
75
  put " catch ("; parse var; puts ') {'
62
76
 
63
77
  first = true
@@ -14,6 +14,17 @@ module Ruby2JS
14
14
 
15
15
  handle :and, :or do |left, right|
16
16
  type = @ast.type
17
+
18
+
19
+ if es2020 and type == :and
20
+ node = rewrite(left, right)
21
+ if node.type == :csend
22
+ return parse right.updated(node.type, node.children)
23
+ else
24
+ left, right = node.children
25
+ end
26
+ end
27
+
17
28
  op_index = operator_index type
18
29
 
19
30
  lgroup = LOGICAL.include?( left.type ) &&
@@ -25,7 +36,7 @@ module Ruby2JS
25
36
  rgroup = true if right.type == :begin
26
37
 
27
38
  put '(' if lgroup; parse left; put ')' if lgroup
28
- put (type==:and ? ' && ' : ' || ')
39
+ put (type==:and ? ' && ' : ((@or == :nullish and es2020) ? ' ?? ' : ' || '))
29
40
  put '(' if rgroup; parse right; put ')' if rgroup
30
41
  end
31
42
 
@@ -51,5 +62,39 @@ module Ruby2JS
51
62
  put '!'; put '(' if group; parse expr; put ')' if group
52
63
  end
53
64
  end
65
+
66
+ # rewrite a && a.b to a&.b
67
+ def rewrite(left, right)
68
+ if left && left.type == :and
69
+ left = rewrite(*left.children)
70
+ end
71
+
72
+ if right.type != :send
73
+ s(:and, left, right)
74
+ elsif conditionally_equals(left, right.children.first)
75
+ # a && a.b => a&.b
76
+ right.updated(:csend, [left, right.children.last])
77
+ elsif conditionally_equals(left.children.last, right.children.first)
78
+ # a && b && b.c => a && b&.c
79
+ left.updated(:and, [left.children.first,
80
+ left.children.last.updated(:csend,
81
+ [left.children.last, right.children.last])])
82
+ else
83
+ s(:and, left, right)
84
+ end
85
+ end
86
+
87
+ # determine if two trees are identical, modulo conditionalilties
88
+ # in other words a.b == a&.b
89
+ def conditionally_equals(left, right)
90
+ if left == right
91
+ true
92
+ elsif !left or !right or left.type != :csend or right.type != :send
93
+ false
94
+ else
95
+ conditionally_equals(left.children.first, right.children.first) &&
96
+ conditionally_equals(left.children.last, right.children.last)
97
+ end
98
+ end
54
99
  end
55
100
  end