ruby2js 3.1.2 → 3.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +52 -11
- data/lib/ruby2js.rb +14 -2
- data/lib/ruby2js/converter.rb +11 -3
- data/lib/ruby2js/converter/args.rb +1 -1
- data/lib/ruby2js/converter/block.rb +2 -2
- data/lib/ruby2js/converter/class.rb +4 -3
- data/lib/ruby2js/converter/class2.rb +38 -9
- data/lib/ruby2js/converter/cvar.rb +1 -1
- data/lib/ruby2js/converter/cvasgn.rb +1 -1
- data/lib/ruby2js/converter/def.rb +2 -2
- data/lib/ruby2js/converter/for.rb +7 -0
- data/lib/ruby2js/converter/hash.rb +14 -3
- data/lib/ruby2js/converter/if.rb +13 -2
- data/lib/ruby2js/converter/import.rb +38 -0
- 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 +53 -27
- data/lib/ruby2js/converter/super.rb +15 -9
- data/lib/ruby2js/converter/xnode.rb +89 -0
- data/lib/ruby2js/es2021.rb +5 -0
- data/lib/ruby2js/es2021/strict.rb +3 -0
- data/lib/ruby2js/filter/cjs.rb +2 -2
- data/lib/ruby2js/filter/esm.rb +72 -0
- data/lib/ruby2js/filter/functions.rb +191 -41
- 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 +15 -15
- data/lib/ruby2js/filter/wunderbar.rb +63 -0
- data/lib/ruby2js/serializer.rb +25 -11
- data/lib/ruby2js/version.rb +2 -2
- data/ruby2js.gemspec +3 -12
- metadata +25 -5
@@ -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
|
data/lib/ruby2js/converter/if.rb
CHANGED
@@ -55,8 +55,19 @@ module Ruby2JS
|
|
55
55
|
end
|
56
56
|
else
|
57
57
|
else_block ||= s(:nil)
|
58
|
-
|
59
|
-
|
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
|
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,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
|
-
|
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
|
-
|
309
|
+
if es2020
|
303
310
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
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
|
-
|
312
|
-
expr = node.updated(:send)
|
313
|
-
result = s(:and, node.children.first, expr)
|
325
|
+
else
|
314
326
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
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
|
-
|
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
|
-
|
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
|