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
@@ -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
@@ -95,15 +95,26 @@ module Ruby2JS
95
95
  elsif not [:lambda, :proc].include? method
96
96
  anonfn = false
97
97
  end
98
+
99
+ # use fat arrow syntax if block contains a reference to 'this'
100
+ if anonfn
101
+ walk = proc do |ast|
102
+ anonfn = false if ast == s(:send, nil, :this)
103
+ ast.children.each do |child|
104
+ walk[child] if child.is_a? Parser::AST::Node
105
+ end
106
+ end
107
+ walk[right]
108
+ end
98
109
  end
99
110
 
100
- if
111
+ if \
101
112
  anonfn and
102
113
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
103
114
  then
104
115
  @prop = left.children.first
105
116
  parse right, :method
106
- elsif
117
+ elsif \
107
118
  es2015 and left.type == :sym and right.type == :lvar and
108
119
  left.children == right.children
109
120
  then
@@ -113,7 +124,7 @@ module Ruby2JS
113
124
  put '['
114
125
  parse left
115
126
  put ']'
116
- elsif
127
+ elsif \
117
128
  left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
118
129
  then
119
130
  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