ruby2js 3.1.2 → 3.3.3

Sign up to get free protection for your applications and to get access to all the features.
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 +14 -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 +15 -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 +3 -12
  39. metadata +25 -5
@@ -0,0 +1,89 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (xnode str hash)
5
+
6
+ # NOTE: xnode is a synthetic
7
+
8
+ handle :xnode do |nodename, *args|
9
+ attrs = {}
10
+ children = []
11
+
12
+ args.each do |arg|
13
+ if arg.type == :hash
14
+ arg.children.each do |pair|
15
+ name = pair.children[0].children[0]
16
+
17
+ if defined? Ruby2JS::Filter::React
18
+ name = :className if name == :class
19
+ name = :htmlFor if name == :for
20
+ end
21
+
22
+ if [:class, :className].include? name and attrs[name]
23
+ if attrs[name].type == :str and pair.children[1].type == :str
24
+ attrs[name] = s(:str, pair.children[1].children[0] + ' ' +
25
+ attrs[name].children[0])
26
+ else
27
+ attrs[name] = s(:send, s(:send, attrs[name], :+,
28
+ s(:str, ' ')), :+, pair.children[1])
29
+ end
30
+ else
31
+ attrs[name] = pair.children[1]
32
+ end
33
+ end
34
+ elsif arg.type == :begin
35
+ children += arg.children
36
+ else
37
+ children << arg
38
+ end
39
+ end
40
+
41
+ put '<'
42
+ put nodename
43
+
44
+ attrs.each do |name, value|
45
+ put ' '
46
+ put name
47
+ put '='
48
+ if value.type == :str
49
+ parse value
50
+ else
51
+ put '{'
52
+ parse value
53
+ put '}'
54
+ end
55
+ end
56
+
57
+ if children.empty?
58
+ put '/>'
59
+ else
60
+ put '>'
61
+ put @nl unless children.length == 1 and children.first.type != :xnode
62
+
63
+ children.each_with_index do |child, index|
64
+ put @nl unless index == 0
65
+ if child.type == :str
66
+ put child.children.first
67
+ elsif child.type == :xnode
68
+ parse child
69
+ else
70
+ begin
71
+ jsx, @jsx = @jsx, true
72
+ put '{'
73
+ parse child
74
+ put '}'
75
+ ensure
76
+ @jsx = jsx
77
+ end
78
+ end
79
+ end
80
+
81
+ put @nl unless children.length == 1 and children.first.type != :xnode
82
+
83
+ put '</'
84
+ put nodename
85
+ put ">"
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2021
4
+ Ruby2JS.eslevel_default = 2021
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2021'
2
+
3
+ Ruby2JS.strict_default = true
@@ -24,7 +24,7 @@ module Ruby2JS
24
24
  *assign.children[1..-1]
25
25
  ])
26
26
 
27
- elsif
27
+ elsif \
28
28
  node.children[2].type == :send and
29
29
  node.children[2].children[0..1] == [nil, :async] and
30
30
  node.children[2].children[2].type == :def
@@ -38,7 +38,7 @@ module Ruby2JS
38
38
  *process_all(fn.children[1..-1])))
39
39
  ])
40
40
 
41
- elsif
41
+ elsif \
42
42
  node.children[2].type == :send and
43
43
  node.children[2].children[0..1] == [nil, :default]
44
44
  then
@@ -0,0 +1,72 @@
1
+ require 'ruby2js'
2
+
3
+ module Ruby2JS
4
+ module Filter
5
+ module ESM
6
+ include SEXP
7
+
8
+ def initialize(*args)
9
+ @esm_include = nil
10
+ super
11
+ end
12
+
13
+ def process(node)
14
+ return super if @esm_include
15
+ @esm_include = Set.new
16
+ @esm_exclude = Set.new
17
+ @esm_export = nil
18
+ result = super
19
+
20
+ esm_walk(result)
21
+
22
+ inventory = (@esm_include - @esm_exclude).to_a.sort
23
+
24
+ if inventory.empty? and not @esm_export
25
+ result
26
+ else
27
+ list = inventory.map do |name|
28
+ if name == "React" and defined? Ruby2JS::Filter::React
29
+ s(:import, "#{name.downcase}", s(:const, nil, name))
30
+ elsif not %w(JSON Object).include? name
31
+ s(:import, "./#{name.downcase}.js", s(:const, nil, name))
32
+ end
33
+ end
34
+
35
+ list.push result
36
+
37
+ if @esm_export
38
+ list.push s(:export, :default, s(:const, nil, @esm_export))
39
+ end
40
+
41
+ s(:begin, *list.compact)
42
+ end
43
+ end
44
+
45
+ # gather constants
46
+ def esm_walk(node)
47
+ # extract ivars and cvars
48
+ if node.type == :const and node.children.first == nil
49
+ @esm_include << node.children.last.to_s
50
+ elsif node.type == :xnode
51
+ name = node.children.first
52
+ @esm_include << name unless name.empty? or name =~ /^[a-z]/
53
+ elsif node.type == :casgn and node.children.first == nil
54
+ @esm_exclude << node.children[1].to_s
55
+ elsif node.type == :class and node.children.first.type == :const
56
+ if node.children.first.children.first == nil
57
+ name = node.children.first.children.last.to_s
58
+ @esm_exclude << name
59
+ @esm_export ||= name
60
+ end
61
+ end
62
+
63
+ # recurse
64
+ node.children.each do |child|
65
+ esm_walk(child) if Parser::AST::Node === child
66
+ end
67
+ end
68
+ end
69
+
70
+ DEFAULTS.push ESM
71
+ end
72
+ end
@@ -1,4 +1,5 @@
1
1
  require 'ruby2js'
2
+ require 'regexp_parser'
2
3
 
3
4
  module Ruby2JS
4
5
  module Filter
@@ -15,6 +16,11 @@ module Ruby2JS
15
16
  gvar: :gvasgn
16
17
  }
17
18
 
19
+ def initialize(*args)
20
+ @jsx = false
21
+ super
22
+ end
23
+
18
24
  def on_send(node)
19
25
  target, method, *args = node.children
20
26
  return super if excluded?(method)
@@ -35,6 +41,78 @@ module Ruby2JS
35
41
  elsif method == :keys and args.length == 0 and node.is_method?
36
42
  process S(:send, s(:const, nil, :Object), :keys, target)
37
43
 
44
+ elsif method == :[]= and args.length == 3 and
45
+ args[0].type == :regexp and args[1].type == :int
46
+ index = args[1].children.first
47
+
48
+ parts = Regexp::Parser.parse(args[0].children.first.children.first)
49
+ split = parts.index do |part|
50
+ part.type == :group and part.number == index
51
+ end
52
+
53
+ return super unless split
54
+
55
+ rewritten = parts[split].to_s
56
+
57
+ dstr = [args.last]
58
+ if rewritten == '()'
59
+ index -= 1
60
+ rewritten = ''
61
+ end
62
+
63
+ parts = parts.to_a
64
+
65
+ pre = ''
66
+ if parts.first.type == :anchor
67
+ pre = parts.shift.to_s
68
+ split -= 1
69
+ end
70
+
71
+ if split > 0
72
+ if split == 1 and parts.first.type == :group
73
+ rewritten = parts.first.to_s + rewritten
74
+ else
75
+ rewritten = '(' + parts[0 .. split - 1].join + ')' + rewritten
76
+ index += 1
77
+ end
78
+ dstr.unshift s(:send, s(:lvar, :match), :[], s(:int, 0))
79
+ end
80
+
81
+
82
+ post = ''
83
+ if parts.last.type == :anchor
84
+ post = parts.pop.to_s
85
+ end
86
+
87
+ if split + 1 < parts.length
88
+ if split + 2 == parts.length and parts.last.type == :group
89
+ rewritten += parts.last.to_s
90
+ else
91
+ rewritten += '(' + parts[split + 1 .. -1].join + ')'
92
+ end
93
+ dstr << s(:send, s(:lvar, :match), :[], s(:int, index))
94
+ end
95
+
96
+ rewritten = pre + rewritten + post
97
+
98
+ regex = process s(:regexp, s(:str, rewritten), args[0].children.last)
99
+ block = s(:block,
100
+ s(:send, target, :replace, regex),
101
+ s(:args, s(:arg, :match)),
102
+ process(s(:dstr, *dstr)))
103
+
104
+ if VAR_TO_ASSIGN.keys.include? target.type
105
+ S(VAR_TO_ASSIGN[target.type], target.children.first, block)
106
+ elsif target.type == :send
107
+ if target.children[0] == nil
108
+ S(:lvasgn, target.children[1], block)
109
+ else
110
+ S(:send, target.children[0], :"#{target.children[1]}=", block)
111
+ end
112
+ else
113
+ super
114
+ end
115
+
38
116
  elsif method == :merge
39
117
  args.unshift target
40
118
 
@@ -50,9 +128,15 @@ module Ruby2JS
50
128
 
51
129
  s(:send, s(:block, s(:send, nil, :lambda), s(:args),
52
130
  s(:begin, *copy, *args.map {|modname|
53
- s(:for, s(:lvasgn, :$_), modname,
54
- s(:send, s(:gvar, :$$), :[]=,
55
- s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
131
+ if modname.type == :hash
132
+ s(:begin, *modname.children.map {|pair|
133
+ s(:send, s(:gvar, :$$), :[]=, *pair.children)
134
+ })
135
+ else
136
+ s(:for, s(:lvasgn, :$_), modname,
137
+ s(:send, s(:gvar, :$$), :[]=,
138
+ s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
139
+ end
56
140
  }, s(:return, s(:gvar, :$$)))), :[])
57
141
  end
58
142
 
@@ -72,9 +156,15 @@ module Ruby2JS
72
156
 
73
157
  s(:send, s(:block, s(:send, nil, :lambda), s(:args),
74
158
  s(:begin, *copy, *args.map {|modname|
75
- s(:for, s(:lvasgn, :$_), modname,
76
- s(:send, target, :[]=,
77
- s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
159
+ if modname.type == :hash
160
+ s(:begin, *modname.children.map {|pair|
161
+ s(:send, target, :[]=, *pair.children)
162
+ })
163
+ else
164
+ s(:for, s(:lvasgn, :$_), modname,
165
+ s(:send, target, :[]=,
166
+ s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
167
+ end
78
168
  }, s(:return, target))), :[])
79
169
  end
80
170
 
@@ -134,23 +224,33 @@ module Ruby2JS
134
224
  [s(:str, Regexp.escape(arg.children.first)), s(:regopt)])
135
225
  end
136
226
 
137
- pattern = arg.children.first.children.first
138
- pattern = pattern.gsub(/\\./, '').gsub(/\[.*\]/, '')
139
-
140
227
  if arg.type == :regexp
228
+ pattern = arg.children.first.children.first
229
+ pattern = pattern.gsub(/\\./, '').gsub(/\[.*\]/, '')
230
+
141
231
  gpattern = arg.updated(:regexp, [*arg.children[0...-1],
142
232
  s(:regopt, :g, *arg.children.last)])
143
233
  else
144
- super
234
+ gpattern = s(:send, s(:const, nil, :RegExp), :new, arg, s(:str, 'g'))
145
235
  end
146
236
 
147
- if pattern.include? '('
148
- s(:block, s(:send,
149
- s(:send, process(target), :match, gpattern), :map),
150
- s(:args, s(:arg, :s)),
151
- s(:return, s(:send, s(:send, s(:lvar, :s), :match, arg),
152
- :slice, s(:int, 1))))
237
+ if arg.type != :regexp or pattern.include? '('
238
+ if es2020
239
+ # Array.from(str.matchAll(/.../g), s => s.slice(1))
240
+ s(:send, s(:const, nil, :Array), :from,
241
+ s(:send, process(target), :matchAll, gpattern),
242
+ s(:block, s(:send, nil, :proc), s(:args, s(:arg, :s)),
243
+ s(:send, s(:lvar, :s), :slice, s(:int, 1))))
244
+ else
245
+ # str.match(/.../g).map(s => s.match(/.../).slice(1))
246
+ s(:block, s(:send,
247
+ s(:send, process(target), :match, gpattern), :map),
248
+ s(:args, s(:arg, :s)),
249
+ s(:return, s(:send, s(:send, s(:lvar, :s), :match, arg),
250
+ :slice, s(:int, 1))))
251
+ end
153
252
  else
253
+ # str.match(/.../g)
154
254
  S(:send, process(target), :match, gpattern)
155
255
  end
156
256
 
@@ -278,6 +378,9 @@ module Ruby2JS
278
378
  end
279
379
 
280
380
 
381
+ elsif method == :[] and target == s(:const, nil, :Hash)
382
+ s(:send, s(:const, nil, :Object), :fromEntries, *process_all(args))
383
+
281
384
  elsif method == :[]
282
385
  # resolve negative literal indexes
283
386
  i = proc do |index|
@@ -295,9 +398,15 @@ module Ruby2JS
295
398
  super
296
399
 
297
400
  elsif index.type == :regexp
298
- process S(:send,
299
- s(:or, S(:send, process(target), :match, index), s(:array)),
300
- :[], args[1] || s(:int, 0))
401
+ if es2020
402
+ process S(:csend,
403
+ S(:send, process(target), :match, index),
404
+ :[], args[1] || s(:int, 0))
405
+ else
406
+ process S(:send,
407
+ s(:or, S(:send, process(target), :match, index), s(:array)),
408
+ :[], args[1] || s(:int, 0))
409
+ end
301
410
 
302
411
  elsif node.children.length != 3
303
412
  super
@@ -307,7 +416,9 @@ module Ruby2JS
307
416
 
308
417
  elsif index.type == :erange
309
418
  start, finish = index.children
310
- if finish.type == :int
419
+ if not finish
420
+ process S(:send, target, :slice, start)
421
+ elsif finish.type == :int
311
422
  process S(:send, target, :slice, i.(start), finish)
312
423
  else
313
424
  process S(:send, target, :slice, i.(start), i.(finish))
@@ -315,7 +426,7 @@ module Ruby2JS
315
426
 
316
427
  elsif index.type == :irange
317
428
  start, finish = index.children
318
- if finish.type == :int
429
+ if finish and finish.type == :int
319
430
  final = S(:int, finish.children.first+1)
320
431
  else
321
432
  final = S(:send, finish, :+, s(:int, 1))
@@ -323,7 +434,7 @@ module Ruby2JS
323
434
 
324
435
  # No need for the last argument if it's -1
325
436
  # This means take all to the end of array
326
- if finish.children.first == -1
437
+ if not finish or finish.children.first == -1
327
438
  process S(:send, target, :slice, start)
328
439
  else
329
440
  process S(:send, target, :slice, start, final)
@@ -395,15 +506,28 @@ module Ruby2JS
395
506
  process node.updated(nil, [s(:const, nil, :Object), :fromEntries,
396
507
  target])
397
508
 
398
- elsif es2019 and method==:rstrip
399
- process node.updated(nil, [target, :trimStart, *args])
509
+ elsif method==:rstrip
510
+ if es2019
511
+ process node.updated(nil, [target, :trimEnd, *args])
512
+ else
513
+ node.updated(nil, [process(target), :replace,
514
+ s(:regexp, s(:str, '\s+\z') , s(:regopt)), s(:str, '')])
515
+ end
400
516
 
401
- elsif es2019 and method==:lstrip
402
- process node.updated(nil, [target, :trimEnd, *args])
517
+ elsif method==:lstrip and args.length == 0
518
+ if es2019
519
+ process s(:send, target, :trimStart)
520
+ else
521
+ node.updated(nil, [process(target), :replace,
522
+ s(:regexp, s(:str, '\A\s+') , s(:regopt)), s(:str, '')])
523
+ end
403
524
 
404
525
  elsif method == :class and args.length==0 and not node.is_method?
405
526
  process node.updated(:attr, [target, :constructor])
406
527
 
528
+ elsif method == :new and target == s(:const, nil, :Exception)
529
+ process S(:send, s(:const, nil, :Error), :new, *args)
530
+
407
531
  else
408
532
  super
409
533
  end
@@ -498,7 +622,7 @@ module Ruby2JS
498
622
  call = call.updated(nil, [s(:begin, range), :step, s(:int, 1)])
499
623
  process node.updated(nil, [call, *node.children[1..-1]])
500
624
 
501
- elsif
625
+ elsif \
502
626
  method == :each and call.children[0].type == :send and
503
627
  call.children[0].children[1] == :step
504
628
  then
@@ -510,7 +634,7 @@ module Ruby2JS
510
634
  :step, step])
511
635
  process node.updated(nil, [call, *node.children[1..-1]])
512
636
 
513
- elsif
637
+ elsif \
514
638
  # (a..b).each {|v| ...}
515
639
  method == :each and
516
640
  call.children[0].type == :begin and
@@ -518,23 +642,34 @@ module Ruby2JS
518
642
  [:irange, :erange].include? call.children[0].children[0].type and
519
643
  node.children[1].children.length == 1
520
644
  then
521
- s(:for, s(:lvasgn, node.children[1].children[0].children[0]),
645
+ process s(:for, s(:lvasgn, node.children[1].children[0].children[0]),
522
646
  call.children[0].children[0], node.children[2])
523
647
 
524
- elsif
525
- [:each, :each_value].include? method and
526
- node.children[1].children.length == 1
648
+ elsif \
649
+ [:each, :each_value].include? method
527
650
  then
528
- if es2015
529
- process node.updated(:for_of,
530
- [s(:lvasgn, node.children[1].children[0].children[0]),
531
- node.children[0].children[0], node.children[2]])
651
+ if es2015 or @jsx
652
+ if node.children[1].children.length > 1
653
+ process node.updated(:for_of,
654
+ [s(:mlhs, *node.children[1].children.map {|child|
655
+ s(:lvasgn, child.children[0])}),
656
+ node.children[0].children[0], node.children[2]])
657
+ elsif node.children[1].children[0].type == :mlhs
658
+ process node.updated(:for_of,
659
+ [s(:mlhs, *node.children[1].children[0].children.map {|child|
660
+ s(:lvasgn, child.children[0])}),
661
+ node.children[0].children[0], node.children[2]])
662
+ else
663
+ process node.updated(:for_of,
664
+ [s(:lvasgn, node.children[1].children[0].children[0]),
665
+ node.children[0].children[0], node.children[2]])
666
+ end
532
667
  else
533
668
  process node.updated(nil, [s(:send, call.children[0],
534
669
  :forEach), *node.children[1..2]])
535
670
  end
536
671
 
537
- elsif
672
+ elsif \
538
673
  method == :each_key and
539
674
  [:each, :each_key].include? method and
540
675
  node.children[1].children.length == 1
@@ -548,10 +683,25 @@ module Ruby2JS
548
683
  s(:block, s(:send, nil, :lambda), *node.children[1..2]),
549
684
  *call.children[2..-1]])
550
685
 
551
- elsif es2017 and method == :each_pair
552
- process node.updated(nil, [s(:send, s(:send, s(:const, nil, :Object),
553
- :entries, call.children[0]), :forEach), s(:args, s(:mlhs,
554
- *node.children[1].children)), node.children[2]])
686
+ elsif method == :each_pair and node.children[1].children.length == 2
687
+ if es2017
688
+ # Object.entries(a).forEach(([key, value]) => {})
689
+ process node.updated(nil, [s(:send, s(:send,
690
+ s(:const, nil, :Object), :entries, call.children[0]), :each),
691
+ node.children[1], node.children[2]])
692
+ else
693
+ # for (key in a). {var value = a[key]; ...}
694
+ process node.updated(:for, [s(:lvasgn,
695
+ node.children[1].children[0].children[0]), call.children[0],
696
+ s(:begin, s(:lvasgn, node.children[1].children[1].children[0],
697
+ s(:send, call.children[0], :[],
698
+ s(:lvar, node.children[1].children[0].children[0]))),
699
+ node.children[2])])
700
+ end
701
+
702
+ elsif method == :scan and call.children.length == 3
703
+ process call.updated(nil, [*call.children, s(:block,
704
+ s(:send, nil, :proc), *node.children[1..-1])])
555
705
 
556
706
  else
557
707
  super