ruby2js 3.0.15 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +92 -7
- data/lib/ruby2js.rb +30 -3
- data/lib/ruby2js/converter.rb +23 -3
- data/lib/ruby2js/converter/args.rb +31 -1
- data/lib/ruby2js/converter/block.rb +2 -2
- data/lib/ruby2js/converter/casgn.rb +3 -2
- data/lib/ruby2js/converter/class.rb +2 -2
- data/lib/ruby2js/converter/class2.rb +117 -13
- data/lib/ruby2js/converter/cvar.rb +5 -3
- data/lib/ruby2js/converter/cvasgn.rb +5 -3
- data/lib/ruby2js/converter/def.rb +2 -2
- data/lib/ruby2js/converter/for.rb +8 -1
- data/lib/ruby2js/converter/hash.rb +22 -6
- data/lib/ruby2js/converter/if.rb +13 -2
- data/lib/ruby2js/converter/import.rb +38 -0
- data/lib/ruby2js/converter/ivar.rb +2 -0
- data/lib/ruby2js/converter/ivasgn.rb +1 -1
- data/lib/ruby2js/converter/kwbegin.rb +16 -2
- data/lib/ruby2js/converter/logical.rb +46 -1
- data/lib/ruby2js/converter/opasgn.rb +5 -2
- data/lib/ruby2js/converter/regexp.rb +27 -2
- data/lib/ruby2js/converter/return.rb +1 -1
- data/lib/ruby2js/converter/send.rb +160 -69
- data/lib/ruby2js/converter/super.rb +15 -9
- data/lib/ruby2js/converter/xnode.rb +89 -0
- data/lib/ruby2js/es2018.rb +5 -0
- data/lib/ruby2js/es2018/strict.rb +3 -0
- data/lib/ruby2js/es2019.rb +5 -0
- data/lib/ruby2js/es2019/strict.rb +3 -0
- data/lib/ruby2js/es2020.rb +5 -0
- data/lib/ruby2js/es2020/strict.rb +3 -0
- data/lib/ruby2js/es2021.rb +5 -0
- data/lib/ruby2js/es2021/strict.rb +3 -0
- data/lib/ruby2js/filter.rb +1 -0
- data/lib/ruby2js/filter/cjs.rb +2 -2
- data/lib/ruby2js/filter/esm.rb +72 -0
- data/lib/ruby2js/filter/functions.rb +218 -34
- data/lib/ruby2js/filter/matchAll.rb +49 -0
- data/lib/ruby2js/filter/node.rb +18 -10
- data/lib/ruby2js/filter/nokogiri.rb +13 -13
- data/lib/ruby2js/filter/react.rb +190 -30
- data/lib/ruby2js/filter/require.rb +1 -4
- data/lib/ruby2js/filter/rubyjs.rb +4 -4
- data/lib/ruby2js/filter/vue.rb +45 -17
- data/lib/ruby2js/filter/wunderbar.rb +63 -0
- data/lib/ruby2js/serializer.rb +25 -11
- data/lib/ruby2js/version.rb +2 -2
- 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
|
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
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
-
|
309
|
+
if es2020
|
289
310
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
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
|
-
|
298
|
-
expr = node.updated(:send)
|
299
|
-
result = s(:and, node.children.first, expr)
|
325
|
+
else
|
300
326
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
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
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
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
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
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
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
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
|
-
|
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
|
16
|
-
args =
|
17
|
-
elsif
|
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 =
|
22
|
+
args = method.children[1].children
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
if es2015
|
25
|
-
if @
|
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
|
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
|
40
|
-
puts ".prototype.#{
|
45
|
+
if method.type != :constructor
|
46
|
+
puts ".prototype.#{ method.children[1].to_s.chomp('=') }"
|
41
47
|
end
|
42
48
|
|
43
49
|
if args
|