ruby2js 3.6.1 → 4.0.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 +1 -7
- data/lib/ruby2js.rb +32 -9
- data/lib/ruby2js/converter.rb +8 -2
- data/lib/ruby2js/converter/assign.rb +159 -0
- data/lib/ruby2js/converter/begin.rb +7 -2
- data/lib/ruby2js/converter/case.rb +7 -2
- data/lib/ruby2js/converter/class.rb +77 -21
- data/lib/ruby2js/converter/class2.rb +39 -11
- data/lib/ruby2js/converter/def.rb +6 -2
- data/lib/ruby2js/converter/dstr.rb +8 -3
- data/lib/ruby2js/converter/hash.rb +9 -5
- data/lib/ruby2js/converter/hide.rb +13 -0
- data/lib/ruby2js/converter/if.rb +10 -2
- data/lib/ruby2js/converter/import.rb +18 -3
- data/lib/ruby2js/converter/kwbegin.rb +9 -2
- data/lib/ruby2js/converter/literal.rb +2 -2
- data/lib/ruby2js/converter/module.rb +37 -5
- data/lib/ruby2js/converter/opasgn.rb +8 -0
- data/lib/ruby2js/converter/send.rb +41 -2
- data/lib/ruby2js/converter/vasgn.rb +5 -0
- data/lib/ruby2js/demo.rb +53 -0
- data/lib/ruby2js/es2022.rb +5 -0
- data/lib/ruby2js/es2022/strict.rb +3 -0
- data/lib/ruby2js/filter.rb +9 -1
- data/lib/ruby2js/filter/active_functions.rb +1 -0
- data/lib/ruby2js/filter/cjs.rb +2 -0
- data/lib/ruby2js/filter/esm.rb +44 -10
- data/lib/ruby2js/filter/functions.rb +84 -95
- data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
- data/lib/ruby2js/filter/react.rb +191 -56
- data/lib/ruby2js/filter/require.rb +100 -5
- data/lib/ruby2js/filter/return.rb +13 -1
- data/lib/ruby2js/filter/stimulus.rb +185 -0
- data/lib/ruby2js/jsx.rb +291 -0
- data/lib/ruby2js/namespace.rb +75 -0
- data/lib/ruby2js/version.rb +3 -3
- metadata +12 -4
@@ -18,7 +18,9 @@ module Ruby2JS
|
|
18
18
|
node.type == :def and node.children.first == :method_missing
|
19
19
|
end
|
20
20
|
|
21
|
-
if
|
21
|
+
if not name
|
22
|
+
put 'class'
|
23
|
+
elsif name.type == :const and name.children.first == nil
|
22
24
|
put 'class '
|
23
25
|
parse name
|
24
26
|
put '$' if proxied
|
@@ -38,7 +40,8 @@ module Ruby2JS
|
|
38
40
|
begin
|
39
41
|
class_name, @class_name = @class_name, name
|
40
42
|
class_parent, @class_parent = @class_parent, inheritance
|
41
|
-
@rbstack.push(
|
43
|
+
@rbstack.push(@namespace.getOwnProps)
|
44
|
+
@rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
|
42
45
|
constructor = []
|
43
46
|
index = 0
|
44
47
|
|
@@ -46,10 +49,17 @@ module Ruby2JS
|
|
46
49
|
body.each do |m|
|
47
50
|
if m.type == :def
|
48
51
|
prop = m.children.first
|
49
|
-
if prop == :initialize
|
52
|
+
if prop == :initialize and !@rbstack.last[:initialize]
|
50
53
|
constructor = m.children[2..-1]
|
51
|
-
elsif
|
52
|
-
@rbstack.last[prop] = s(:self)
|
54
|
+
elsif prop.to_s.end_with? '='
|
55
|
+
@rbstack.last[prop.to_s[0..-2].to_sym] = s(:autobind, s(:self))
|
56
|
+
else
|
57
|
+
@rbstack.last[prop] = m.is_method? ? s(:autobind, s(:self)) : s(:self)
|
58
|
+
end
|
59
|
+
elsif m.type == :send and m.children[0..1] == [nil, :async]
|
60
|
+
if m.children[2].type == :def
|
61
|
+
prop = m.children[2].children.first
|
62
|
+
@rbstack.last[prop] = s(:autobind, s(:self))
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -146,10 +156,10 @@ module Ruby2JS
|
|
146
156
|
end
|
147
157
|
end
|
148
158
|
|
149
|
-
if m.type == :def || m.type == :async
|
159
|
+
if m.type == :def || m.type == :defm || m.type == :async
|
150
160
|
@prop = m.children.first
|
151
161
|
|
152
|
-
if @prop == :initialize
|
162
|
+
if @prop == :initialize and !@rbstack.last[:initialize]
|
153
163
|
@prop = :constructor
|
154
164
|
|
155
165
|
if constructor == [] or constructor == [(:super)]
|
@@ -234,13 +244,27 @@ module Ruby2JS
|
|
234
244
|
else
|
235
245
|
if m.children[1] == :include
|
236
246
|
m = m.updated(:begin, m.children[2..-1].map {|mname|
|
237
|
-
|
238
|
-
s(:attr, name, :prototype), mname)
|
247
|
+
@namespace.defineProps @namespace.find(mname)
|
248
|
+
s(:assign, s(:attr, name, :prototype), mname)
|
249
|
+
})
|
239
250
|
end
|
240
251
|
|
241
252
|
skipped = true
|
242
253
|
end
|
243
254
|
|
255
|
+
elsif es2022 and \
|
256
|
+
m.type == :send and m.children.first.type == :self and \
|
257
|
+
m.children[1].to_s.end_with? '='
|
258
|
+
|
259
|
+
put 'static '
|
260
|
+
parse m.updated(:lvasgn, [m.children[1].to_s.sub('=', ''),
|
261
|
+
m.children[2]])
|
262
|
+
|
263
|
+
elsif m.type == :defineProps
|
264
|
+
skipped = true
|
265
|
+
@namespace.defineProps m.children.first
|
266
|
+
@rbstack.last.merge! m.children.first
|
267
|
+
|
244
268
|
else
|
245
269
|
if m.type == :cvasgn and !underscored_private
|
246
270
|
put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
|
@@ -263,7 +287,7 @@ module Ruby2JS
|
|
263
287
|
end
|
264
288
|
|
265
289
|
if skipped
|
266
|
-
post << [m, comments]
|
290
|
+
post << [m, comments] unless m.type == :defineProps
|
267
291
|
else
|
268
292
|
comments.reverse.each {|comment| insert location, comment}
|
269
293
|
end
|
@@ -300,6 +324,10 @@ module Ruby2JS
|
|
300
324
|
else
|
301
325
|
parse m.updated(:send, [@class_name, *m.children[1..-1]])
|
302
326
|
end
|
327
|
+
elsif m.type == :block and m.children.first.children.first == nil
|
328
|
+
# class method calls passing a block
|
329
|
+
parse s(:block, s(:send, name, *m.children.first.children[1..-1]),
|
330
|
+
*m.children[1..-1])
|
303
331
|
else
|
304
332
|
parse m, :statement
|
305
333
|
end
|
@@ -345,7 +373,7 @@ module Ruby2JS
|
|
345
373
|
ensure
|
346
374
|
@class_name = class_name
|
347
375
|
@class_parent = class_parent
|
348
|
-
@rbstack.pop
|
376
|
+
@namespace.defineProps @rbstack.pop
|
349
377
|
end
|
350
378
|
end
|
351
379
|
end
|
@@ -6,7 +6,7 @@ module Ruby2JS
|
|
6
6
|
# (arg :x)
|
7
7
|
# (...)
|
8
8
|
|
9
|
-
handle :def, :defm, :async do |name, args, body=nil|
|
9
|
+
handle :def, :defm, :async, :deff do |name, args, body=nil|
|
10
10
|
body ||= s(:begin)
|
11
11
|
|
12
12
|
add_implicit_block = false
|
@@ -106,7 +106,7 @@ module Ruby2JS
|
|
106
106
|
# es2015 fat arrow support
|
107
107
|
if \
|
108
108
|
not name and es2015 and @state != :method and @ast.type != :defm and
|
109
|
-
not @prop
|
109
|
+
@ast.type != :deff and not @prop
|
110
110
|
then
|
111
111
|
expr = body
|
112
112
|
expr = expr.children.first while expr.type == :autoreturn
|
@@ -118,6 +118,10 @@ module Ruby2JS
|
|
118
118
|
if EXPRESSIONS.include? expr.type
|
119
119
|
if expr.type == :send and expr.children[0..1] == [nil, :raise]
|
120
120
|
style = :statement
|
121
|
+
elsif expr.type == :send and expr.children.length == 2 and
|
122
|
+
expr.children.first == nil and @rbstack.last and
|
123
|
+
@rbstack.last[expr.children[1]]&.type == :autobind
|
124
|
+
style = :statement
|
121
125
|
else
|
122
126
|
style = :expression
|
123
127
|
end
|
@@ -10,6 +10,11 @@ module Ruby2JS
|
|
10
10
|
# (...))
|
11
11
|
|
12
12
|
handle :dstr, :dsym do |*children|
|
13
|
+
if @state == :expression and children.empty?
|
14
|
+
puts '""'
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
13
18
|
if es2015
|
14
19
|
# gather length of string parts; if long enough, newlines will
|
15
20
|
# not be escaped (poor man's HEREDOC)
|
@@ -26,7 +31,7 @@ module Ruby2JS
|
|
26
31
|
else
|
27
32
|
put str
|
28
33
|
end
|
29
|
-
|
34
|
+
elsif child != s(:begin)
|
30
35
|
put '${'
|
31
36
|
parse child
|
32
37
|
put '}'
|
@@ -40,8 +45,8 @@ module Ruby2JS
|
|
40
45
|
children.each_with_index do |child, index|
|
41
46
|
put ' + ' unless index == 0
|
42
47
|
|
43
|
-
if child.type == :begin and child.children.length
|
44
|
-
child = child.children.first
|
48
|
+
if child.type == :begin and child.children.length <= 1
|
49
|
+
child = child.children.first || s(:str, '')
|
45
50
|
end
|
46
51
|
|
47
52
|
if child.type == :send
|
@@ -73,7 +73,7 @@ module Ruby2JS
|
|
73
73
|
if right.type == :hash
|
74
74
|
right.children.each do |pair|
|
75
75
|
next unless Parser::AST::Node === pair.children.last
|
76
|
-
if [
|
76
|
+
if %i[block def defm async].include? pair.children.last.type
|
77
77
|
if @comments[pair.children.last]
|
78
78
|
(puts ''; singleton = false) if singleton
|
79
79
|
comments(pair.children.last).each do |comment|
|
@@ -120,22 +120,26 @@ module Ruby2JS
|
|
120
120
|
|
121
121
|
if \
|
122
122
|
anonfn and
|
123
|
-
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\
|
123
|
+
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
|
124
124
|
then
|
125
125
|
@prop = left.children.first
|
126
126
|
parse right, :method
|
127
127
|
elsif \
|
128
|
-
es2015 and left.type == :sym and right.type == :lvar
|
129
|
-
|
128
|
+
es2015 and left.type == :sym and (right.type == :lvar or
|
129
|
+
(right.type == :send and right.children.first == nil)) and
|
130
|
+
left.children.last == right.children.last
|
130
131
|
then
|
131
132
|
parse right
|
133
|
+
elsif right.type == :defm and %i[sym str].include? left.type and es2015
|
134
|
+
@prop = left.children.first.to_s
|
135
|
+
parse right
|
132
136
|
else
|
133
137
|
if not [:str, :sym].include? left.type and es2015
|
134
138
|
put '['
|
135
139
|
parse left
|
136
140
|
put ']'
|
137
141
|
elsif \
|
138
|
-
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\
|
142
|
+
left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
|
139
143
|
then
|
140
144
|
put left.children.first
|
141
145
|
else
|
data/lib/ruby2js/converter/if.rb
CHANGED
@@ -64,10 +64,18 @@ module Ruby2JS
|
|
64
64
|
if else_block.type == :begin
|
65
65
|
else_block = s(:xnode, '', *else_block.children)
|
66
66
|
end
|
67
|
+
else
|
68
|
+
if then_block.type == :begin
|
69
|
+
then_block = s(:kwbegin, then_block)
|
70
|
+
end
|
71
|
+
|
72
|
+
if else_block.type == :begin
|
73
|
+
else_block = s(:kwbegin, else_block)
|
74
|
+
end
|
67
75
|
end
|
68
76
|
|
69
|
-
parse condition; put ' ? '; parse then_block
|
70
|
-
put ' : '; parse else_block
|
77
|
+
parse condition; put ' ? '; parse then_block, @state
|
78
|
+
put ' : '; parse else_block, @state
|
71
79
|
end
|
72
80
|
end
|
73
81
|
end
|
@@ -28,14 +28,29 @@ module Ruby2JS
|
|
28
28
|
put path.inspect
|
29
29
|
else
|
30
30
|
# import (x) from "file.js"
|
31
|
-
default_import = !args.first.is_a?(Array) && [
|
31
|
+
default_import = !args.first.is_a?(Array) && %i[const send attr str].include?(args.first.type)
|
32
|
+
|
33
|
+
if default_import and args.length > 1
|
34
|
+
parse args.shift
|
35
|
+
put ', '
|
36
|
+
default_import = false
|
37
|
+
end
|
38
|
+
|
32
39
|
args = args.first if args.first.is_a?(Array)
|
33
40
|
|
41
|
+
if args.first.type == :array
|
42
|
+
args = args.first.children
|
43
|
+
end
|
44
|
+
|
34
45
|
# handle the default name or { ConstA, Const B } portion
|
35
46
|
put "{ " unless default_import
|
36
47
|
args.each_with_index do |arg, index|
|
37
48
|
put ', ' unless index == 0
|
38
|
-
|
49
|
+
if arg.type == :str
|
50
|
+
put arg.children.first # useful for '*'
|
51
|
+
else
|
52
|
+
parse arg
|
53
|
+
end
|
39
54
|
end
|
40
55
|
put " }" unless default_import
|
41
56
|
|
@@ -43,7 +58,7 @@ module Ruby2JS
|
|
43
58
|
|
44
59
|
# should there be an as clause? e.g., import React as *
|
45
60
|
if path.is_a?(Array) && !path[0].is_a?(String) && path[0].type == :pair && path[0].children[0].children[0] == :as
|
46
|
-
put " as #{path[0].children[1].children
|
61
|
+
put " as #{path[0].children[1].children.last}"
|
47
62
|
|
48
63
|
# advance to the next kwarg, aka from
|
49
64
|
from_kwarg_position = 1
|
@@ -6,7 +6,7 @@ module Ruby2JS
|
|
6
6
|
# (resbody nil nil
|
7
7
|
# (send nil :b)) nil)
|
8
8
|
handle :rescue do |*statements|
|
9
|
-
|
9
|
+
parse s(:kwbegin, s(:rescue, *statements)), @state
|
10
10
|
end
|
11
11
|
|
12
12
|
# (kwbegin
|
@@ -19,7 +19,14 @@ module Ruby2JS
|
|
19
19
|
|
20
20
|
handle :kwbegin do |*children|
|
21
21
|
block = children.first
|
22
|
-
|
22
|
+
|
23
|
+
if @state == :expression
|
24
|
+
parse s(:send, s(:block, s(:send, nil, :proc), s(:args),
|
25
|
+
s(:begin, s(:autoreturn, *children))), :[])
|
26
|
+
return
|
27
|
+
end
|
28
|
+
|
29
|
+
if block&.type == :ensure
|
23
30
|
block, finally = block.children
|
24
31
|
else
|
25
32
|
finally = nil
|
@@ -20,8 +20,8 @@ module Ruby2JS
|
|
20
20
|
def number_format(number)
|
21
21
|
return number.to_s unless es2021
|
22
22
|
parts = number.to_s.split('.')
|
23
|
-
parts[0].gsub
|
24
|
-
parts[1].gsub
|
23
|
+
parts[0] = parts[0].gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1_")
|
24
|
+
parts[1] = parts[1].gsub(/(\d\d\d)(?=\d)/, "\\1_") if parts[1]
|
25
25
|
parts.join('.')
|
26
26
|
end
|
27
27
|
end
|
@@ -4,10 +4,22 @@ module Ruby2JS
|
|
4
4
|
# (module
|
5
5
|
# (const nil :A)
|
6
6
|
# (...)
|
7
|
+
#
|
8
|
+
# Note: modules_hash is an anonymous modules as a value in a hash; the
|
9
|
+
# name has already been output so should be ignored other than
|
10
|
+
# in determining the namespace.
|
11
|
+
|
12
|
+
handle :module, :module_hash do |name, *body|
|
13
|
+
extend = @namespace.enter(name)
|
7
14
|
|
8
|
-
handle :module do |name, *body|
|
9
15
|
if body == [nil]
|
10
|
-
|
16
|
+
if @ast.type == :module and not extend
|
17
|
+
parse @ast.updated(:casgn, [*name.children, s(:hash)])
|
18
|
+
else
|
19
|
+
parse @ast.updated(:hash, [])
|
20
|
+
end
|
21
|
+
|
22
|
+
@namespace.leave
|
11
23
|
return
|
12
24
|
end
|
13
25
|
|
@@ -15,8 +27,20 @@ module Ruby2JS
|
|
15
27
|
body = body.first.children
|
16
28
|
end
|
17
29
|
|
18
|
-
if body.length > 0 and body.all? {|child|
|
19
|
-
|
30
|
+
if body.length > 0 and body.all? {|child|
|
31
|
+
%i[def module].include? child.type or
|
32
|
+
(es2015 and child.type == :class and child.children[1] == nil)}
|
33
|
+
|
34
|
+
if extend
|
35
|
+
parse s(:assign, name, @ast.updated(:class_module,
|
36
|
+
[nil, nil, *body])), :statement
|
37
|
+
elsif @ast.type == :module_hash
|
38
|
+
parse @ast.updated(:class_module, [nil, nil, *body])
|
39
|
+
else
|
40
|
+
parse @ast.updated(:class_module, [name, nil, *body])
|
41
|
+
end
|
42
|
+
|
43
|
+
@namespace.leave
|
20
44
|
return
|
21
45
|
end
|
22
46
|
|
@@ -47,6 +71,8 @@ module Ruby2JS
|
|
47
71
|
symbols << node.children.first
|
48
72
|
elsif node.type == :class and node.children.first.children.first == nil
|
49
73
|
symbols << node.children.first.children.last
|
74
|
+
elsif node.type == :module
|
75
|
+
symbols << node.children.first.children.last
|
50
76
|
end
|
51
77
|
end
|
52
78
|
|
@@ -55,11 +81,17 @@ module Ruby2JS
|
|
55
81
|
|
56
82
|
body = s(:send, s(:block, s(:send, nil, :proc), s(:args),
|
57
83
|
s(:begin, *body)), :[])
|
58
|
-
if name
|
84
|
+
if not name
|
85
|
+
parse body
|
86
|
+
elsif extend
|
87
|
+
parse s(:assign, name, body)
|
88
|
+
elsif name.children.first == nil
|
59
89
|
parse s(:lvasgn, name.children.last, body)
|
60
90
|
else
|
61
91
|
parse s(:send, name.children.first, "#{name.children.last}=", body)
|
62
92
|
end
|
93
|
+
|
94
|
+
@namespace.leave
|
63
95
|
end
|
64
96
|
end
|
65
97
|
end
|
@@ -12,6 +12,14 @@ 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 var.type == :lvar
|
16
|
+
name = var.children.first
|
17
|
+
receiver = @rbstack.map {|rb| rb[name]}.compact.last
|
18
|
+
if receiver
|
19
|
+
var = s(:attr, nil, name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
15
23
|
if \
|
16
24
|
[:+, :-].include?(op) and value.type==:int and
|
17
25
|
(value.children==[1] or value.children==[-1])
|
@@ -41,6 +41,20 @@ module Ruby2JS
|
|
41
41
|
# strip '!' and '?' decorations
|
42
42
|
method = method.to_s[0..-2] if method =~ /\w[!?]$/
|
43
43
|
|
44
|
+
# anonymous class
|
45
|
+
if method == :new and receiver and receiver.children == [nil, :Class] and
|
46
|
+
args.last.type == :def and args.last.children.first == nil
|
47
|
+
|
48
|
+
parent = (args.length > 1) ? args.first : nil
|
49
|
+
|
50
|
+
if es2015
|
51
|
+
return parse s(:class2, nil, parent, *args.last.children[2..-1])
|
52
|
+
else
|
53
|
+
return parse s(:kwbegin, s(:class, s(:const, nil, :$$), parent,
|
54
|
+
*args.last.children[2..-1]), s(:const, nil, :$$))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
44
58
|
# three ways to define anonymous functions
|
45
59
|
if method == :new and receiver and receiver.children == [nil, :Proc]
|
46
60
|
return parse args.first, @state
|
@@ -118,8 +132,13 @@ module Ruby2JS
|
|
118
132
|
|
119
133
|
# resolve anonymous receivers against rbstack
|
120
134
|
receiver ||= @rbstack.map {|rb| rb[method]}.compact.last
|
135
|
+
autobind = nil
|
121
136
|
|
122
137
|
if receiver
|
138
|
+
if receiver.type == :autobind
|
139
|
+
autobind = receiver = receiver.children.first
|
140
|
+
end
|
141
|
+
|
123
142
|
group_receiver = receiver.type == :send &&
|
124
143
|
op_index < operator_index( receiver.children[1] ) if receiver
|
125
144
|
group_receiver ||= GROUP_OPERATORS.include? receiver.type
|
@@ -176,9 +195,15 @@ module Ruby2JS
|
|
176
195
|
receiver.type == :send and
|
177
196
|
receiver.children[1] == :+@ and
|
178
197
|
Parser::AST::Node === receiver.children[0] and
|
179
|
-
receiver.children[0].type
|
198
|
+
%i(class module).include? receiver.children[0].type
|
180
199
|
then
|
181
|
-
|
200
|
+
if receiver.children[0].type == :class
|
201
|
+
parse receiver.children[0].updated(:class_extend)
|
202
|
+
else
|
203
|
+
mod = receiver.children[0]
|
204
|
+
parse s(:assign, mod.children[0],
|
205
|
+
mod.updated(nil, [nil, *mod.children[1..-1]]))
|
206
|
+
end
|
182
207
|
else
|
183
208
|
put method.to_s[0]; parse receiver
|
184
209
|
end
|
@@ -281,6 +306,12 @@ module Ruby2JS
|
|
281
306
|
else
|
282
307
|
put 'await ' if @ast.type == :await
|
283
308
|
|
309
|
+
if method == :bind and receiver&.type == :send
|
310
|
+
if receiver.children.length == 2 and receiver.children.first == nil
|
311
|
+
receiver = receiver.updated(:attr) # prevent autobind
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
284
315
|
if not ast.is_method? and ast.type != :send!
|
285
316
|
if receiver
|
286
317
|
(group_receiver ? group(receiver) : parse(receiver))
|
@@ -303,6 +334,14 @@ module Ruby2JS
|
|
303
334
|
compact { puts "("; parse_all(*args, join: ",#@ws"); sput ')' }
|
304
335
|
end
|
305
336
|
end
|
337
|
+
|
338
|
+
if autobind and not ast.is_method? and ast.type != :attr
|
339
|
+
if @state == :statement
|
340
|
+
put '()'
|
341
|
+
else
|
342
|
+
put '.bind('; parse(autobind); put ')'
|
343
|
+
end
|
344
|
+
end
|
306
345
|
end
|
307
346
|
end
|
308
347
|
|