ruby2js 3.0.15 → 3.3.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 (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