ruby2js 3.1.1 → 3.3.2

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +52 -11
  3. data/lib/ruby2js.rb +14 -2
  4. data/lib/ruby2js/converter.rb +11 -3
  5. data/lib/ruby2js/converter/args.rb +1 -1
  6. data/lib/ruby2js/converter/block.rb +2 -2
  7. data/lib/ruby2js/converter/class.rb +4 -3
  8. data/lib/ruby2js/converter/class2.rb +38 -9
  9. data/lib/ruby2js/converter/cvar.rb +1 -1
  10. data/lib/ruby2js/converter/cvasgn.rb +1 -1
  11. data/lib/ruby2js/converter/def.rb +2 -2
  12. data/lib/ruby2js/converter/for.rb +7 -0
  13. data/lib/ruby2js/converter/hash.rb +3 -3
  14. data/lib/ruby2js/converter/if.rb +13 -2
  15. data/lib/ruby2js/converter/import.rb +38 -0
  16. data/lib/ruby2js/converter/logical.rb +46 -1
  17. data/lib/ruby2js/converter/opasgn.rb +5 -2
  18. data/lib/ruby2js/converter/regexp.rb +27 -2
  19. data/lib/ruby2js/converter/return.rb +1 -1
  20. data/lib/ruby2js/converter/send.rb +53 -27
  21. data/lib/ruby2js/converter/super.rb +15 -9
  22. data/lib/ruby2js/converter/xnode.rb +89 -0
  23. data/lib/ruby2js/es2021.rb +5 -0
  24. data/lib/ruby2js/es2021/strict.rb +3 -0
  25. data/lib/ruby2js/filter/cjs.rb +2 -2
  26. data/lib/ruby2js/filter/esm.rb +72 -0
  27. data/lib/ruby2js/filter/functions.rb +191 -41
  28. data/lib/ruby2js/filter/matchAll.rb +49 -0
  29. data/lib/ruby2js/filter/node.rb +18 -10
  30. data/lib/ruby2js/filter/nokogiri.rb +13 -13
  31. data/lib/ruby2js/filter/react.rb +190 -30
  32. data/lib/ruby2js/filter/require.rb +1 -4
  33. data/lib/ruby2js/filter/rubyjs.rb +4 -4
  34. data/lib/ruby2js/filter/vue.rb +40 -15
  35. data/lib/ruby2js/filter/wunderbar.rb +63 -0
  36. data/lib/ruby2js/serializer.rb +25 -11
  37. data/lib/ruby2js/version.rb +2 -2
  38. data/ruby2js.gemspec +2 -11
  39. metadata +24 -4
@@ -7,7 +7,7 @@ module Ruby2JS
7
7
  handle :cvasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
- prefix = es2020 ? '#' : '_'
10
+ prefix = es2020 ? '#$' : '_'
11
11
 
12
12
  if @class_name
13
13
  parse @class_name
@@ -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,6 +10,13 @@ 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
@@ -97,13 +97,13 @@ module Ruby2JS
97
97
  end
98
98
  end
99
99
 
100
- if
100
+ if \
101
101
  anonfn and
102
102
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
103
103
  then
104
104
  @prop = left.children.first
105
105
  parse right, :method
106
- elsif
106
+ elsif \
107
107
  es2015 and left.type == :sym and right.type == :lvar and
108
108
  left.children == right.children
109
109
  then
@@ -113,7 +113,7 @@ module Ruby2JS
113
113
  put '['
114
114
  parse left
115
115
  put ']'
116
- elsif
116
+ elsif \
117
117
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
118
118
  then
119
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
@@ -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
@@ -12,7 +12,7 @@ module Ruby2JS
12
12
  var = s(:lvar, var.children.first) if var.type == :lvasgn
13
13
  var = s(:cvar, var.children.first) if var.type == :cvasgn
14
14
 
15
- if
15
+ if \
16
16
  [:+, :-].include?(op) and value.type==:int and
17
17
  (value.children==[1] or value.children==[-1])
18
18
  then
@@ -46,7 +46,10 @@ module Ruby2JS
46
46
  vtype = :ivar if asgn.type == :ivasgn
47
47
  vtype = :cvar if asgn.type == :cvasgn
48
48
 
49
- if vtype
49
+ if es2021
50
+ op = type == :and ? '&&' : (@or == :nullish ? '??' : '||')
51
+ parse s(:op_asgn, asgn, op, value);
52
+ elsif vtype
50
53
  parse s(asgn.type, asgn.children.first, s(type,
51
54
  s(vtype, asgn.children.first), value))
52
55
  elsif asgn.type == :send and asgn.children[1] == :[]
@@ -27,10 +27,35 @@ module Ruby2JS
27
27
  end
28
28
  end
29
29
 
30
+ # in Ruby regular expressions, ^ and $ apply to each line
30
31
  if parts.first.type == :str and parts.first.children[0].start_with?('^')
31
- opts = opts + [:m] unless opts.include? :m or opts.include? 'm'
32
+ if opts.include? :m or opts.include? 'm'
33
+ if parts.first.children[0].gsub(/\\./, '').gsub(/\[.*?\]/, '').include? '.'
34
+ opts = opts + [:s] unless opts.include? :s or opts.include? 's'
35
+ end
36
+ else
37
+ opts = opts + [:m]
38
+ end
32
39
  elsif parts.last.type == :str and parts.last.children[0].end_with?('$')
33
- opts = opts + [:m] unless opts.include? :m or opts.include? 'm'
40
+ if opts.include? :m or opts.include? 'm'
41
+ if parts.last.children[0].gsub(/\\./, '').gsub(/\[.*?\]/, '').include? '.'
42
+ opts = opts + [:s] unless opts.include? :s or opts.include? 's'
43
+ end
44
+ else
45
+ opts = opts + [:m]
46
+ end
47
+ end
48
+
49
+ # in Ruby regular expressions, /A is the start of the string
50
+ if parts.first.type == :str and parts.first.children[0].start_with?('\A')
51
+ parts = [s(:str, parts.first.children[0].sub('\A', '^'))] +
52
+ parts[1..-1]
53
+ end
54
+
55
+ # in Ruby regular expressions, /z is the end of the string
56
+ if parts.last.type == :str and parts.last.children[0].end_with?('\z')
57
+ parts = parts[0..-2] +
58
+ [s(:str, parts.first.children[0].sub('\z', '$'))]
34
59
  end
35
60
 
36
61
  # use slash syntax if there are few embedded slashes in the regexp
@@ -14,7 +14,7 @@ module Ruby2JS
14
14
 
15
15
  EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send, :attr,
16
16
  :str, :sym, :dstr, :dsym, :cvar, :ivar, :zsuper, :super, :or, :and,
17
- :block, :const, :true, :false ]
17
+ :block, :const, :true, :false, :xnode ]
18
18
 
19
19
  handle :autoreturn do |*statements|
20
20
  return if statements == [nil]
@@ -16,7 +16,7 @@ module Ruby2JS
16
16
  handle :send, :sendw, :await, :attr, :call do |receiver, method, *args|
17
17
  ast = @ast
18
18
 
19
- if
19
+ if \
20
20
  args.length == 1 and method == :+
21
21
  then
22
22
  node = collapse_strings(ast)
@@ -25,7 +25,7 @@ module Ruby2JS
25
25
 
26
26
  # :irange support
27
27
  # - currently only .to_a
28
- if
28
+ if \
29
29
  receiver and
30
30
  receiver.type == :begin and
31
31
  [:irange, :erange].include? receiver.children.first.type
@@ -43,6 +43,7 @@ module Ruby2JS
43
43
  # three ways to define anonymous functions
44
44
  if method == :new and receiver and receiver.children == [nil, :Proc]
45
45
  return parse args.first, @state
46
+
46
47
  elsif not receiver and [:lambda, :proc].include? method
47
48
  if method == :lambda
48
49
  return parse s(args.first.type, *args.first.children[0..-2],
@@ -86,7 +87,7 @@ module Ruby2JS
86
87
  # async proc {|x| ... }
87
88
  return parse block.updated(:async, [nil, *block.children[1..-1]])
88
89
 
89
- elsif
90
+ elsif \
90
91
  block.children[0].children[1] == :new and
91
92
  block.children[0].children[0] == s(:const, nil, :Proc)
92
93
  then
@@ -141,7 +142,7 @@ module Ruby2JS
141
142
 
142
143
  elsif method == :[]
143
144
  (group_receiver ? group(receiver) : parse(receiver))
144
- if
145
+ if \
145
146
  args.length == 1 and [:str, :sym].include? args.first.type and
146
147
  args.first.children.first.to_s =~ /^[a-zA-Z]\w*$/
147
148
  then
@@ -152,7 +153,7 @@ module Ruby2JS
152
153
 
153
154
  elsif method == :[]=
154
155
  parse receiver
155
- if
156
+ if \
156
157
  args.length == 2 and [:str, :sym].include? args.first.type and
157
158
  args.first.children.first.to_s =~ /^[a-zA-Z]\w*$/
158
159
  then
@@ -170,7 +171,7 @@ module Ruby2JS
170
171
  put ')'
171
172
 
172
173
  elsif [:-@, :+@, :~, '~'].include? method
173
- if
174
+ if \
174
175
  receiver.type == :send and
175
176
  receiver.children[1] == :+@ and
176
177
  Parser::AST::Node === receiver.children[0] and
@@ -196,7 +197,13 @@ module Ruby2JS
196
197
 
197
198
  elsif OPERATORS.flatten.include?(method) and not LOGICAL.include?(method)
198
199
  (group_receiver ? group(receiver) : parse(receiver))
199
- put " #{ method } "
200
+
201
+ if @comparison == :identity and [:==, :!=].include? method
202
+ put " #{ method }= "
203
+ else
204
+ put " #{ method } "
205
+ end
206
+
200
207
  (group_target ? group(target) : parse(target))
201
208
 
202
209
  elsif method =~ /=$/
@@ -249,7 +256,7 @@ module Ruby2JS
249
256
  elsif args.length == 1 and args.first.type == :const
250
257
  # accommodation for JavaScript like new syntax w/o argument list
251
258
  parse s(:attr, args.first, :new), @state
252
- elsif
259
+ elsif \
253
260
  args.length == 2 and [:send, :const].include? args.first.type and
254
261
  args.last.type == :def and args.last.children.first == nil
255
262
  then
@@ -299,27 +306,46 @@ module Ruby2JS
299
306
  end
300
307
 
301
308
  handle :csend do |receiver, method, *args|
302
- node = @ast
309
+ if es2020
303
310
 
304
- # collect up chain of conditional sends
305
- stack = []
306
- while node.children.first.type == :csend
307
- stack << node
308
- node = node.children.first
309
- end
311
+ # optional chaining
312
+ parse receiver
313
+ put "?."
314
+ if method == :[]
315
+ put '['
316
+ args.each {|arg| parse arg}
317
+ put ']'
318
+ else
319
+ put method.to_s
320
+ put '(' if @ast.is_method?
321
+ args.each {|arg| parse arg}
322
+ put ')' if @ast.is_method?
323
+ end
310
324
 
311
- # conditionally evaluate most nested expression
312
- expr = node.updated(:send)
313
- result = s(:and, node.children.first, expr)
325
+ else
314
326
 
315
- # build up chain of conditional evaluations
316
- until stack.empty?
317
- node = stack.pop
318
- expr = node.updated(:send, [expr, *node.children[1..-1]])
319
- result = s(:and, result, expr)
320
- end
327
+ node = @ast
328
+
329
+ # collect up chain of conditional sends
330
+ stack = []
331
+ while node.children.first.type == :csend
332
+ stack << node
333
+ node = node.children.first
334
+ end
321
335
 
322
- parse result
336
+ # conditionally evaluate most nested expression
337
+ expr = node.updated(:send)
338
+ result = s(:and, node.children.first, expr)
339
+
340
+ # build up chain of conditional evaluations
341
+ until stack.empty?
342
+ node = stack.pop
343
+ expr = node.updated(:send, [expr, *node.children[1..-1]])
344
+ result = s(:and, result, expr)
345
+ end
346
+
347
+ parse result
348
+ end
323
349
  end
324
350
 
325
351
  handle :splat do |expr|
@@ -334,7 +360,7 @@ module Ruby2JS
334
360
  right = node.children[2]
335
361
 
336
362
  # recursively evaluate left hand side
337
- if
363
+ if \
338
364
  left.type == :send and left.children.length == 3 and
339
365
  left.children[1] == :+
340
366
  then
@@ -342,7 +368,7 @@ module Ruby2JS
342
368
  end
343
369
 
344
370
  # recursively evaluate right hand side
345
- if
371
+ if \
346
372
  right.type == :send and right.children.length == 3 and
347
373
  right.children[1] == :+
348
374
  then
@@ -6,27 +6,33 @@ module Ruby2JS
6
6
  # (super ...)
7
7
 
8
8
  handle :super, :zsuper do |*args|
9
- unless @instance_method and @class_parent
9
+ method = @instance_method || @class_method
10
+
11
+ unless method and @class_parent
10
12
  raise Error.new("super outside of a method", @ast)
11
13
  end
12
14
 
13
15
  # what to pass
14
16
  if @ast.type == :zsuper
15
- if @instance_method.type == :method
16
- args = @instance_method.children[2].children[1].children
17
- elsif @instance_method.type == :prop
17
+ if method.type == :method
18
+ args = method.children[2].children[1].children
19
+ elsif method.type == :prop
18
20
  args = nil
19
21
  else
20
- args = @instance_method.children[1].children
22
+ args = method.children[1].children
21
23
  end
22
24
  end
23
25
 
24
26
  if es2015
25
- if @instance_method.children[0] == :constructor
27
+ if @class_method
28
+ parse @class_parent
29
+ put '.'
30
+ put method.children[0]
31
+ elsif method.children[0] == :constructor
26
32
  put 'super'
27
33
  else
28
34
  put 'super.'
29
- put @instance_method.children[0]
35
+ put method.children[0]
30
36
  end
31
37
 
32
38
  put '('
@@ -36,8 +42,8 @@ module Ruby2JS
36
42
  parse @class_parent
37
43
 
38
44
  # what to call
39
- if @instance_method.type != :constructor
40
- puts ".prototype.#{ @instance_method.children[1].to_s.chomp('=') }"
45
+ if method.type != :constructor
46
+ puts ".prototype.#{ method.children[1].to_s.chomp('=') }"
41
47
  end
42
48
 
43
49
  if args