ruby2js 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +44 -4
  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 +2 -2
  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 +142 -26
  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 +1 -1
  38. metadata +10 -3
@@ -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
@@ -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