ruby2js 3.0.15 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|