ruby2js 3.0.15 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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,12 +16,26 @@ module Ruby2JS
16
16
  handle :send, :sendw, :await, :attr, :call do |receiver, method, *args|
17
17
  ast = @ast
18
18
 
19
- if
20
- args.length == 1 and method == :+
21
- then
22
- node = collapse_strings(ast)
23
- return parse node if node != ast
24
- end
19
+ if \
20
+ args.length == 1 and method == :+
21
+ then
22
+ node = collapse_strings(ast)
23
+ return parse node if node != ast
24
+ end
25
+
26
+ # :irange support
27
+ # - currently only .to_a
28
+ if \
29
+ receiver and
30
+ receiver.type == :begin and
31
+ [:irange, :erange].include? receiver.children.first.type
32
+ then
33
+ unless method == :to_a
34
+ raise Error.new("#{receiver.children.first.type} can only be converted to array currently", receiver.children.first)
35
+ else
36
+ return range_to_array(receiver.children.first)
37
+ end
38
+ end
25
39
 
26
40
  # strip '!' and '?' decorations
27
41
  method = method.to_s[0..-2] if method =~ /\w[!?]$/
@@ -29,6 +43,7 @@ module Ruby2JS
29
43
  # three ways to define anonymous functions
30
44
  if method == :new and receiver and receiver.children == [nil, :Proc]
31
45
  return parse args.first, @state
46
+
32
47
  elsif not receiver and [:lambda, :proc].include? method
33
48
  if method == :lambda
34
49
  return parse s(args.first.type, *args.first.children[0..-2],
@@ -39,7 +54,7 @@ module Ruby2JS
39
54
  end
40
55
 
41
56
  # call anonymous function
42
- if [:call, :[]].include? method and receiver and receiver.type == :block
57
+ if [:call, :[]].include? method and receiver and receiver.type == :block
43
58
  t2,m2,*args2 = receiver.children.first.children
44
59
  if not t2 and [:lambda, :proc].include? m2 and args2.length == 0
45
60
  (es2015 || @state == :statement ? group(receiver) : parse(receiver))
@@ -72,7 +87,7 @@ module Ruby2JS
72
87
  # async proc {|x| ... }
73
88
  return parse block.updated(:async, [nil, *block.children[1..-1]])
74
89
 
75
- elsif
90
+ elsif \
76
91
  block.children[0].children[1] == :new and
77
92
  block.children[0].children[0] == s(:const, nil, :Proc)
78
93
  then
@@ -97,7 +112,7 @@ module Ruby2JS
97
112
 
98
113
  op_index = operator_index method
99
114
  if op_index != -1
100
- target = args.first
115
+ target = args.first
101
116
  end
102
117
 
103
118
  # resolve anonymous receivers against rbstack
@@ -109,7 +124,7 @@ module Ruby2JS
109
124
  group_receiver ||= GROUP_OPERATORS.include? receiver.type
110
125
  group_receiver = false if receiver.children[1] == :[]
111
126
  if receiver.type == :int and !OPERATORS.flatten.include?(method)
112
- group_receiver = true
127
+ group_receiver = true
113
128
  end
114
129
  if not receiver.is_method? and receiver.children.last == :new
115
130
  group_receiver = true
@@ -117,7 +132,7 @@ module Ruby2JS
117
132
  end
118
133
 
119
134
  if target
120
- group_target = target.type == :send &&
135
+ group_target = target.type == :send &&
121
136
  op_index < operator_index( target.children[1] )
122
137
  group_target ||= GROUP_OPERATORS.include? target.type
123
138
  end
@@ -127,7 +142,7 @@ module Ruby2JS
127
142
 
128
143
  elsif method == :[]
129
144
  (group_receiver ? group(receiver) : parse(receiver))
130
- if
145
+ if \
131
146
  args.length == 1 and [:str, :sym].include? args.first.type and
132
147
  args.first.children.first.to_s =~ /^[a-zA-Z]\w*$/
133
148
  then
@@ -138,7 +153,7 @@ module Ruby2JS
138
153
 
139
154
  elsif method == :[]=
140
155
  parse receiver
141
- if
156
+ if \
142
157
  args.length == 2 and [:str, :sym].include? args.first.type and
143
158
  args.first.children.first.to_s =~ /^[a-zA-Z]\w*$/
144
159
  then
@@ -156,7 +171,7 @@ module Ruby2JS
156
171
  put ')'
157
172
 
158
173
  elsif [:-@, :+@, :~, '~'].include? method
159
- if
174
+ if \
160
175
  receiver.type == :send and
161
176
  receiver.children[1] == :+@ and
162
177
  Parser::AST::Node === receiver.children[0] and
@@ -182,7 +197,13 @@ module Ruby2JS
182
197
 
183
198
  elsif OPERATORS.flatten.include?(method) and not LOGICAL.include?(method)
184
199
  (group_receiver ? group(receiver) : parse(receiver))
185
- put " #{ method } "
200
+
201
+ if @comparison == :identity and [:==, :!=].include? method
202
+ put " #{ method }= "
203
+ else
204
+ put " #{ method } "
205
+ end
206
+
186
207
  (group_target ? group(target) : parse(target))
187
208
 
188
209
  elsif method =~ /=$/
@@ -235,7 +256,7 @@ module Ruby2JS
235
256
  elsif args.length == 1 and args.first.type == :const
236
257
  # accommodation for JavaScript like new syntax w/o argument list
237
258
  parse s(:attr, args.first, :new), @state
238
- elsif
259
+ elsif \
239
260
  args.length == 2 and [:send, :const].include? args.first.type and
240
261
  args.last.type == :def and args.last.children.first == nil
241
262
  then
@@ -269,7 +290,7 @@ module Ruby2JS
269
290
  parse ast.updated(:lvasgn, [method]), @state
270
291
  end
271
292
  elsif args.any? {|arg| arg.type == :splat} and not es2015
272
- parse s(:send, s(:attr, receiver, method), :apply,
293
+ parse s(:send, s(:attr, receiver, method), :apply,
273
294
  (receiver || s(:nil)), s(:array, *args))
274
295
  else
275
296
  (group_receiver ? group(receiver) : parse(receiver))
@@ -285,27 +306,46 @@ module Ruby2JS
285
306
  end
286
307
 
287
308
  handle :csend do |receiver, method, *args|
288
- node = @ast
309
+ if es2020
289
310
 
290
- # collect up chain of conditional sends
291
- stack = []
292
- while node.children.first.type == :csend
293
- stack << node
294
- node = node.children.first
295
- 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
296
324
 
297
- # conditionally evaluate most nested expression
298
- expr = node.updated(:send)
299
- result = s(:and, node.children.first, expr)
325
+ else
300
326
 
301
- # build up chain of conditional evaluations
302
- until stack.empty?
303
- node = stack.pop
304
- expr = node.updated(:send, [expr, *node.children[1..-1]])
305
- result = s(:and, result, expr)
306
- 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
307
335
 
308
- 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
309
349
  end
310
350
 
311
351
  handle :splat do |expr|
@@ -313,47 +353,98 @@ module Ruby2JS
313
353
  parse expr
314
354
  end
315
355
 
316
- # do string concatenation when possible
317
- def collapse_strings(node)
318
- left = node.children[0]
319
- return node unless left
320
- right = node.children[2]
321
-
322
- # recursively evaluate left hand side
323
- if
324
- left.type == :send and left.children.length == 3 and
325
- left.children[1] == :+
326
- then
327
- left = collapse_strings(left)
328
- end
356
+ # do string concatenation when possible
357
+ def collapse_strings(node)
358
+ left = node.children[0]
359
+ return node unless left
360
+ right = node.children[2]
361
+
362
+ # recursively evaluate left hand side
363
+ if \
364
+ left.type == :send and left.children.length == 3 and
365
+ left.children[1] == :+
366
+ then
367
+ left = collapse_strings(left)
368
+ end
329
369
 
330
- # recursively evaluate right hand side
331
- if
332
- right.type == :send and right.children.length == 3 and
333
- right.children[1] == :+
334
- then
335
- right = collapse_strings(right)
336
- end
370
+ # recursively evaluate right hand side
371
+ if \
372
+ right.type == :send and right.children.length == 3 and
373
+ right.children[1] == :+
374
+ then
375
+ right = collapse_strings(right)
376
+ end
337
377
 
338
- # if left and right are both strings, perform concatenation
339
- if [:dstr, :str].include? left.type and [:dstr, :str].include? right.type
340
- if left.type == :str and right.type == :str
341
- return left.updated nil,
342
- [left.children.first + right.children.first]
378
+ # if left and right are both strings, perform concatenation
379
+ if [:dstr, :str].include? left.type and [:dstr, :str].include? right.type
380
+ if left.type == :str and right.type == :str
381
+ return left.updated nil,
382
+ [left.children.first + right.children.first]
383
+ else
384
+ left = s(:dstr, left) if left.type == :str
385
+ right = s(:dstr, right) if right.type == :str
386
+ return left.updated(nil, left.children + right.children)
387
+ end
388
+ end
389
+
390
+ # if left and right are unchanged, return original node; otherwise
391
+ # return node modified to include new left and/or right hand sides.
392
+ if left == node.children[0] and right == node.children[2]
393
+ return node
343
394
  else
344
- left = s(:dstr, left) if left.type == :str
345
- right = s(:dstr, right) if right.type == :str
346
- return left.updated(nil, left.children + right.children)
395
+ return node.updated(nil, [left, :+, right])
347
396
  end
348
397
  end
349
398
 
350
- # if left and right are unchanged, return original node; otherwise
351
- # return node modified to include new left and/or right hand sides.
352
- if left == node.children[0] and right == node.children[2]
353
- return node
354
- else
355
- return node.updated(nil, [left, :+, right])
399
+ def range_to_array(node)
400
+ start, finish = node.children
401
+ if start.type == :int and start.children.first == 0
402
+ # Ranges which start from 0 can be achieved with more simpler code
403
+ if finish.type == :int
404
+ # output cleaner code if we know the value already
405
+ length = finish.children.first + (node.type == :irange ? 1 : 0)
406
+ else
407
+ # If this is variable we need to fix indexing by 1 in js
408
+ length = "#{finish.children.last}" + (node.type == :irange ? "+1" : "")
409
+ end
410
+
411
+ if es2015
412
+ return put "[...Array(#{length}).keys()]"
413
+ else
414
+ return put "Array.apply(null, {length: #{length}}).map(Function.call, Number)"
415
+ end
416
+ else
417
+ # Use .compact because the first argument is nil with variables
418
+ # This way the first value is always set
419
+ start_value = start.children.compact.first
420
+ finish_value = finish.children.compact.first
421
+ if start.type == :int and finish.type == :int
422
+ length = finish_value - start_value + (node.type == :irange ? 1 : 0)
423
+ else
424
+ length = "(#{finish_value}-#{start_value}" + (node.type == :irange ? "+1" : "") + ")"
425
+ end
426
+
427
+ # Avoid of using same variables in the map as used in the irange or elsewhere in this code
428
+ # Ruby2js only allows dollar sign in beginning of variable so i$ is safe
429
+ if @vars.include? :idx or start_value == :idx or finish_value == :idx
430
+ index_var = 'i$'
431
+ else
432
+ index_var = 'idx'
433
+ end
434
+
435
+ if es2015
436
+ # Use _ because it's normal convention in JS for variable which is not used at all
437
+ if @vars.include? :_ or start_value == :_ or finish_value == :_
438
+ blank = '_$'
439
+ else
440
+ blank = '_'
441
+ end
442
+
443
+ return put "Array.from({length: #{length}}, (#{blank}, #{index_var}) => #{index_var}+#{start_value})"
444
+ else
445
+ return put "Array.apply(null, {length: #{length}}).map(Function.call, Number).map(function (#{index_var}) { return #{index_var}+#{start_value} })"
446
+ end
447
+ end
356
448
  end
357
449
  end
358
- end
359
450
  end
@@ -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