ruby2js 3.5.2 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -665
- data/lib/ruby2js.rb +47 -13
- data/lib/ruby2js/converter.rb +9 -3
- data/lib/ruby2js/converter/args.rb +6 -1
- 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 +80 -21
- data/lib/ruby2js/converter/class2.rb +107 -33
- data/lib/ruby2js/converter/def.rb +7 -3
- data/lib/ruby2js/converter/dstr.rb +8 -3
- data/lib/ruby2js/converter/hash.rb +28 -6
- data/lib/ruby2js/converter/hide.rb +13 -0
- data/lib/ruby2js/converter/if.rb +10 -2
- data/lib/ruby2js/converter/import.rb +19 -4
- data/lib/ruby2js/converter/kwbegin.rb +9 -2
- data/lib/ruby2js/converter/literal.rb +14 -2
- data/lib/ruby2js/converter/logical.rb +1 -1
- data/lib/ruby2js/converter/module.rb +41 -4
- data/lib/ruby2js/converter/opasgn.rb +8 -0
- data/lib/ruby2js/converter/return.rb +2 -1
- data/lib/ruby2js/converter/send.rb +73 -8
- data/lib/ruby2js/converter/vasgn.rb +5 -0
- data/lib/ruby2js/converter/xstr.rb +2 -3
- 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 +44 -0
- data/lib/ruby2js/filter/camelCase.rb +6 -3
- data/lib/ruby2js/filter/cjs.rb +2 -0
- data/lib/ruby2js/filter/esm.rb +118 -26
- data/lib/ruby2js/filter/functions.rb +104 -106
- data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
- data/lib/ruby2js/filter/node.rb +58 -14
- data/lib/ruby2js/filter/nokogiri.rb +12 -12
- data/lib/ruby2js/filter/react.rb +192 -57
- data/lib/ruby2js/filter/require.rb +102 -11
- data/lib/ruby2js/filter/return.rb +13 -1
- data/lib/ruby2js/filter/stimulus.rb +185 -0
- data/lib/ruby2js/jsx.rb +309 -0
- data/lib/ruby2js/namespace.rb +75 -0
- data/lib/ruby2js/rails.rb +15 -9
- data/lib/ruby2js/serializer.rb +3 -1
- data/lib/ruby2js/version.rb +3 -3
- data/ruby2js.gemspec +2 -2
- metadata +17 -9
- data/lib/ruby2js/filter/esm_migration.rb +0 -72
- data/lib/ruby2js/filter/fast-deep-equal.rb +0 -23
@@ -1,17 +1,19 @@
|
|
1
1
|
require 'ruby2js'
|
2
2
|
|
3
|
+
# Convert Wunderbar syntax to JSX
|
4
|
+
|
3
5
|
module Ruby2JS
|
4
6
|
module Filter
|
5
|
-
module
|
7
|
+
module JSX
|
6
8
|
include SEXP
|
7
9
|
|
8
10
|
def on_send(node)
|
9
|
-
target, method, *
|
11
|
+
target, method, *args = node.children
|
10
12
|
|
11
13
|
if target == s(:const, nil, :Wunderbar)
|
12
14
|
if [:debug, :info, :warn, :error, :fatal].include? method
|
13
15
|
method = :error if method == :fatal
|
14
|
-
return node.updated(nil, [s(:const, nil, :console), method, *
|
16
|
+
return node.updated(nil, [s(:const, nil, :console), method, *args])
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
@@ -27,7 +29,17 @@ module Ruby2JS
|
|
27
29
|
end
|
28
30
|
|
29
31
|
if target == nil and method.to_s.start_with? "_"
|
30
|
-
S(:xnode,
|
32
|
+
S(:xnode, method.to_s[1..-1], *stack, *process_all(args))
|
33
|
+
|
34
|
+
elsif method == :createElement and target == s(:const, nil, :React)
|
35
|
+
if args.first.type == :str and \
|
36
|
+
(args.length == 1 or %i(nil hash).include? args[1].type)
|
37
|
+
attrs = (args[1]&.type != :nil && args[1]) || s(:hash)
|
38
|
+
S(:xnode, args[0].children.first, attrs, *process_all(args[2..-1]))
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
|
31
43
|
else
|
32
44
|
super
|
33
45
|
end
|
@@ -42,8 +54,18 @@ module Ruby2JS
|
|
42
54
|
|
43
55
|
if target == nil and method.to_s.start_with? "_"
|
44
56
|
if args.children.empty?
|
45
|
-
|
46
|
-
|
57
|
+
if method == :_
|
58
|
+
# Fragment
|
59
|
+
if send.children.length == 2
|
60
|
+
process send.updated(:xnode, ['', *process_all(block)])
|
61
|
+
else
|
62
|
+
process s(:xnode, 'React.Fragment', *send.children[2..-1],
|
63
|
+
*process_all(block))
|
64
|
+
end
|
65
|
+
else
|
66
|
+
# append block as a standalone proc
|
67
|
+
process send.updated(nil, [*send.children, *process_all(block)])
|
68
|
+
end
|
47
69
|
else
|
48
70
|
# iterate over Enumerable arguments if there are args present
|
49
71
|
send = send.children
|
@@ -58,6 +80,6 @@ module Ruby2JS
|
|
58
80
|
end
|
59
81
|
end
|
60
82
|
|
61
|
-
DEFAULTS.push
|
83
|
+
DEFAULTS.push JSX
|
62
84
|
end
|
63
85
|
end
|
data/lib/ruby2js/filter/node.rb
CHANGED
@@ -14,6 +14,10 @@ module Ruby2JS
|
|
14
14
|
|
15
15
|
IMPORT_FS = s(:import, ['fs'], s(:attr, nil, :fs))
|
16
16
|
|
17
|
+
IMPORT_OS = s(:import, ['os'], s(:attr, nil, :os))
|
18
|
+
|
19
|
+
IMPORT_PATH = s(:import, ['path'], s(:attr, nil, :path))
|
20
|
+
|
17
21
|
SETUP_ARGV = s(:lvasgn, :ARGV, s(:send, s(:attr,
|
18
22
|
s(:attr, nil, :process), :argv), :slice, s(:int, 2)))
|
19
23
|
|
@@ -31,11 +35,11 @@ module Ruby2JS
|
|
31
35
|
prepend_list << IMPORT_CHILD_PROCESS
|
32
36
|
|
33
37
|
if args.length == 1
|
34
|
-
|
38
|
+
S(:send, s(:attr, nil, :child_process), :execSync,
|
35
39
|
process(args.first),
|
36
40
|
s(:hash, s(:pair, s(:sym, :stdio), s(:str, 'inherit'))))
|
37
41
|
else
|
38
|
-
|
42
|
+
S(:send, s(:attr, nil, :child_process), :execFileSync,
|
39
43
|
process(args.first), s(:array, *process_all(args[1..-1])),
|
40
44
|
s(:hash, s(:pair, s(:sym, :stdio), s(:str, 'inherit'))))
|
41
45
|
end
|
@@ -57,7 +61,7 @@ module Ruby2JS
|
|
57
61
|
then
|
58
62
|
if method == :read and args.length == 1
|
59
63
|
prepend_list << IMPORT_FS
|
60
|
-
|
64
|
+
S(:send, s(:attr, nil, :fs), :readFileSync, *process_all(args),
|
61
65
|
s(:str, 'utf8'))
|
62
66
|
|
63
67
|
elsif method == :write and args.length == 2
|
@@ -111,15 +115,15 @@ module Ruby2JS
|
|
111
115
|
|
112
116
|
elsif method == :symlink and args.length == 2
|
113
117
|
prepend_list << IMPORT_FS
|
114
|
-
|
118
|
+
S(:send, s(:attr, nil, :fs), :symlinkSync, *process_all(args))
|
115
119
|
|
116
120
|
elsif method == :truncate and args.length == 2
|
117
121
|
prepend_list << IMPORT_FS
|
118
|
-
|
122
|
+
S(:send, s(:attr, nil, :fs), :truncateSync, *process_all(args))
|
119
123
|
|
120
124
|
elsif [:stat, :lstat].include? method and args.length == 1
|
121
125
|
prepend_list << IMPORT_FS
|
122
|
-
|
126
|
+
S(:send, s(:attr, nil, :fs), method.to_s + 'Sync',
|
123
127
|
process(args.first))
|
124
128
|
|
125
129
|
elsif method == :unlink and args.length == 1
|
@@ -128,6 +132,29 @@ module Ruby2JS
|
|
128
132
|
S(:send, s(:attr, nil, :fs), :unlinkSync, process(file))
|
129
133
|
})
|
130
134
|
|
135
|
+
elsif target.children.last == :File
|
136
|
+
if method == :absolute_path
|
137
|
+
prepend_list << IMPORT_PATH
|
138
|
+
S(:send, s(:attr, nil, :path), :resolve,
|
139
|
+
*process_all(args.reverse))
|
140
|
+
elsif method == :absolute_path?
|
141
|
+
prepend_list << IMPORT_PATH
|
142
|
+
S(:send, s(:attr, nil, :path), :isAbsolute, *process_all(args))
|
143
|
+
elsif method == :basename
|
144
|
+
prepend_list << IMPORT_PATH
|
145
|
+
S(:send, s(:attr, nil, :path), :basename, *process_all(args))
|
146
|
+
elsif method == :dirname
|
147
|
+
prepend_list << IMPORT_PATH
|
148
|
+
S(:send, s(:attr, nil, :path), :dirname, *process_all(args))
|
149
|
+
elsif method == :extname
|
150
|
+
prepend_list << IMPORT_PATH
|
151
|
+
S(:send, s(:attr, nil, :path), :extname, *process_all(args))
|
152
|
+
elsif method == :join
|
153
|
+
prepend_list << IMPORT_PATH
|
154
|
+
S(:send, s(:attr, nil, :path), :join, *process_all(args))
|
155
|
+
else
|
156
|
+
super
|
157
|
+
end
|
131
158
|
else
|
132
159
|
super
|
133
160
|
end
|
@@ -163,7 +190,7 @@ module Ruby2JS
|
|
163
190
|
S(:send, s(:attr, nil, :process), :chdir, *process_all(args))
|
164
191
|
|
165
192
|
elsif method == :pwd and args.length == 0
|
166
|
-
|
193
|
+
S(:send!, s(:attr, nil, :process), :cwd)
|
167
194
|
|
168
195
|
elsif method == :rmdir and args.length == 1
|
169
196
|
prepend_list << IMPORT_FS
|
@@ -173,11 +200,11 @@ module Ruby2JS
|
|
173
200
|
|
174
201
|
elsif method == :ln and args.length == 2
|
175
202
|
prepend_list << IMPORT_FS
|
176
|
-
|
203
|
+
S(:send, s(:attr, nil, :fs), :linkSync, *process_all(args))
|
177
204
|
|
178
205
|
elsif method == :ln_s and args.length == 2
|
179
206
|
prepend_list << IMPORT_FS
|
180
|
-
|
207
|
+
S(:send, s(:attr, nil, :fs), :symlinkSync, *process_all(args))
|
181
208
|
|
182
209
|
elsif method == :rm and args.length == 1
|
183
210
|
prepend_list << IMPORT_FS
|
@@ -224,16 +251,16 @@ module Ruby2JS
|
|
224
251
|
if method == :chdir and args.length == 1
|
225
252
|
S(:send, s(:attr, nil, :process), :chdir, *process_all(args))
|
226
253
|
elsif method == :pwd and args.length == 0
|
227
|
-
|
254
|
+
S(:send!, s(:attr, nil, :process), :cwd)
|
228
255
|
elsif method == :entries
|
229
256
|
prepend_list << IMPORT_FS
|
230
|
-
|
257
|
+
S(:send, s(:attr, nil, :fs), :readdirSync, *process_all(args))
|
231
258
|
elsif method == :mkdir and args.length == 1
|
232
259
|
prepend_list << IMPORT_FS
|
233
|
-
|
260
|
+
S(:send, s(:attr, nil, :fs), :mkdirSync, process(args.first))
|
234
261
|
elsif method == :rmdir and args.length == 1
|
235
262
|
prepend_list << IMPORT_FS
|
236
|
-
|
263
|
+
S(:send, s(:attr, nil, :fs), :rmdirSync, process(args.first))
|
237
264
|
elsif method == :mktmpdir and args.length <=1
|
238
265
|
prepend_list << IMPORT_FS
|
239
266
|
if args.length == 0
|
@@ -244,7 +271,14 @@ module Ruby2JS
|
|
244
271
|
prefix = args.first
|
245
272
|
end
|
246
273
|
|
247
|
-
|
274
|
+
S(:send, s(:attr, nil, :fs), :mkdtempSync, process(prefix))
|
275
|
+
elsif method == :home and args.length == 0
|
276
|
+
prepend_list << IMPORT_OS
|
277
|
+
S(:send!, s(:attr, nil, :os), :homedir)
|
278
|
+
elsif method == :tmpdir and args.length == 0
|
279
|
+
prepend_list << IMPORT_OS
|
280
|
+
S(:send!, s(:attr, nil, :os), :tmpdir)
|
281
|
+
|
248
282
|
else
|
249
283
|
super
|
250
284
|
end
|
@@ -285,6 +319,16 @@ module Ruby2JS
|
|
285
319
|
S(:attr, s(:attr, nil, :process), :stdout)
|
286
320
|
elsif node.children == [nil, :STDERR]
|
287
321
|
S(:attr, s(:attr, nil, :process), :stderr)
|
322
|
+
elsif node.children.first == s(:const, nil, :File)
|
323
|
+
if node.children.last == :SEPARATOR
|
324
|
+
prepend_list << IMPORT_PATH
|
325
|
+
S(:attr, s(:attr, nil, :path), :sep)
|
326
|
+
elsif node.children.last == :PATH_SEPARATOR
|
327
|
+
prepend_list << IMPORT_PATH
|
328
|
+
S(:attr, s(:attr, nil, :path), :delimiter)
|
329
|
+
else
|
330
|
+
super
|
331
|
+
end
|
288
332
|
else
|
289
333
|
super
|
290
334
|
end
|
@@ -47,40 +47,40 @@ module Ruby2JS
|
|
47
47
|
method == :at and
|
48
48
|
args.length == 1 and args.first.type == :str
|
49
49
|
then
|
50
|
-
S(:send, target, :querySelector, process(args.first))
|
50
|
+
S(:send, process(target), :querySelector, process(args.first))
|
51
51
|
|
52
52
|
elsif \
|
53
53
|
method == :search and
|
54
54
|
args.length == 1 and args.first.type == :str
|
55
55
|
then
|
56
|
-
S(:send, target, :querySelectorAll, process(args.first))
|
56
|
+
S(:send, process(target), :querySelectorAll, process(args.first))
|
57
57
|
|
58
58
|
elsif method === :parent and args.length == 0
|
59
|
-
S(:attr, target, :parentNode)
|
59
|
+
S(:attr, process(target), :parentNode)
|
60
60
|
|
61
61
|
elsif method === :name and args.length == 0
|
62
|
-
S(:attr, target, :nodeName)
|
62
|
+
S(:attr, process(target), :nodeName)
|
63
63
|
|
64
64
|
elsif [:text, :content].include? method and args.length == 0
|
65
|
-
S(:attr, target, :textContent)
|
65
|
+
S(:attr, process(target), :textContent)
|
66
66
|
|
67
67
|
elsif method == :content= and args.length == 1
|
68
|
-
S(:send, target, :textContent=, *process_all(args))
|
68
|
+
S(:send, process(target), :textContent=, *process_all(args))
|
69
69
|
|
70
70
|
elsif method === :inner_html and args.length == 0
|
71
|
-
S(:attr, target, :innerHTML)
|
71
|
+
S(:attr, process(target), :innerHTML)
|
72
72
|
|
73
73
|
elsif method == :inner_html= and args.length == 1
|
74
|
-
S(:send, target, :innerHTML=, *process_all(args))
|
74
|
+
S(:send, process(target), :innerHTML=, *process_all(args))
|
75
75
|
|
76
76
|
elsif method === :to_html and args.length == 0
|
77
|
-
S(:attr, target, :outerHTML)
|
77
|
+
S(:attr, process(target), :outerHTML)
|
78
78
|
|
79
79
|
elsif \
|
80
80
|
[:attr, :get_attribute].include? method and
|
81
81
|
args.length == 1 and args.first.type == :str
|
82
82
|
then
|
83
|
-
S(:send, target, :getAttribute, process(args.first))
|
83
|
+
S(:send, process(target), :getAttribute, process(args.first))
|
84
84
|
|
85
85
|
elsif \
|
86
86
|
[:key?, :has_attribute].include? method and
|
@@ -154,14 +154,14 @@ module Ruby2JS
|
|
154
154
|
[:add_next_sibling, :next=, :after].include? method and
|
155
155
|
args.length == 1
|
156
156
|
then
|
157
|
-
S(:send, s(:attr, target, :parentNode), :insertBefore,
|
157
|
+
S(:send, s(:attr, process(target), :parentNode), :insertBefore,
|
158
158
|
process(args.first), s(:attr, target, :nextSibling))
|
159
159
|
|
160
160
|
elsif \
|
161
161
|
[:add_previous_sibling, :previous=, :before].include? method and
|
162
162
|
args.length == 1
|
163
163
|
then
|
164
|
-
S(:send, s(:attr, target, :parentNode), :insertBefore,
|
164
|
+
S(:send, s(:attr, process(target), :parentNode), :insertBefore,
|
165
165
|
process(args.first), target)
|
166
166
|
|
167
167
|
elsif method == :prepend_child and args.length == 1
|
data/lib/ruby2js/filter/react.rb
CHANGED
@@ -17,30 +17,39 @@
|
|
17
17
|
# * ~"x" becomes document.querySelector("x")
|
18
18
|
#
|
19
19
|
require 'ruby2js'
|
20
|
+
require 'ruby2js/jsx'
|
20
21
|
|
21
22
|
module Ruby2JS
|
22
23
|
module Filter
|
23
24
|
module React
|
24
25
|
include SEXP
|
26
|
+
extend SEXP
|
27
|
+
|
28
|
+
REACT_IMPORTS = {
|
29
|
+
React: s(:import, ['React'], s(:attr, nil, :React)),
|
30
|
+
ReactDOM: s(:import, ['ReactDOM'], s(:attr, nil, :ReactDOM))
|
31
|
+
}
|
25
32
|
|
26
33
|
# the following command can be used to generate ReactAttrs:
|
27
34
|
#
|
28
35
|
# ruby -r ruby2js/filter/react -e "Ruby2JS::Filter::React.genAttrs"
|
29
36
|
#
|
30
37
|
def self.genAttrs
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
unless RUBY_ENGINE == 'opal'
|
39
|
+
require 'nokogumbo'
|
40
|
+
page = 'https://facebook.github.io/react/docs/tags-and-attributes.html'
|
41
|
+
doc = Nokogiri::HTML5.get(page)
|
42
|
+
|
43
|
+
# delete contents of page prior to the list of supported attributes
|
44
|
+
attrs = doc.at('a[name=supported-attributes]')
|
45
|
+
attrs = attrs.parent while attrs and not attrs.name.start_with? 'h'
|
46
|
+
attrs.previous_sibling.remove while attrs and attrs.previous_sibling
|
47
|
+
|
48
|
+
# extract attribute names with uppercase chars from code and format
|
49
|
+
attrs = doc.search('code').map(&:text).join(' ')
|
50
|
+
attrs = attrs.split(/\s+/).grep(/[A-Z]/).sort.uniq.join(' ')
|
51
|
+
puts "ReactAttrs = %w(#{attrs})".gsub(/(.{1,72})(\s+|\Z)/, "\\1\n")
|
52
|
+
end
|
44
53
|
end
|
45
54
|
|
46
55
|
# list of react attributes that require special processing
|
@@ -60,8 +69,13 @@ module Ruby2JS
|
|
60
69
|
xlinkActuate xlinkArcrole xlinkHref xlinkRole xlinkShow xlinkTitle
|
61
70
|
xlinkType xmlBase xmlLang xmlSpace)
|
62
71
|
|
72
|
+
ReactLifecycle = %w(render componentDidMount shouldComponentUpdate
|
73
|
+
getShapshotBeforeUpdate componentDidUpdate componentWillUnmount
|
74
|
+
componentDidCatch)
|
75
|
+
|
63
76
|
ReactAttrMap = Hash[ReactAttrs.map {|name| [name.downcase, name]}]
|
64
77
|
ReactAttrMap['for'] = 'htmlFor'
|
78
|
+
ReactFragment = :'_React.Fragment'
|
65
79
|
|
66
80
|
def initialize(*args)
|
67
81
|
@react = nil
|
@@ -71,6 +85,7 @@ module Ruby2JS
|
|
71
85
|
@react_props = []
|
72
86
|
@react_methods = []
|
73
87
|
@react_filter_functions = false
|
88
|
+
@react_imports = false
|
74
89
|
@jsx = false
|
75
90
|
super
|
76
91
|
end
|
@@ -88,8 +103,17 @@ module Ruby2JS
|
|
88
103
|
end
|
89
104
|
|
90
105
|
if \
|
91
|
-
defined? Ruby2JS::Filter::
|
92
|
-
filters.include? Ruby2JS::Filter::
|
106
|
+
(defined? Ruby2JS::Filter::ESM and
|
107
|
+
filters.include? Ruby2JS::Filter::ESM) or
|
108
|
+
(defined? Ruby2JS::Filter::CJS and
|
109
|
+
filters.include? Ruby2JS::Filter::CJS)
|
110
|
+
then
|
111
|
+
@react_imports = true
|
112
|
+
end
|
113
|
+
|
114
|
+
if \
|
115
|
+
defined? Ruby2JS::Filter::JSX and
|
116
|
+
filters.include? Ruby2JS::Filter::JSX
|
93
117
|
then
|
94
118
|
@jsx = true
|
95
119
|
end
|
@@ -106,8 +130,11 @@ module Ruby2JS
|
|
106
130
|
return super unless cname.children.first == nil
|
107
131
|
return super unless inheritance == s(:const, nil, :React) or
|
108
132
|
inheritance == s(:const, nil, :Vue) or
|
133
|
+
inheritance == s(:const, s(:const, nil, :React), :Component) or
|
109
134
|
inheritance == s(:send, s(:const, nil, :React), :Component)
|
110
135
|
|
136
|
+
prepend_list << REACT_IMPORTS[:React] if @react_imports
|
137
|
+
|
111
138
|
# traverse down to actual list of class statements
|
112
139
|
if body.length == 1
|
113
140
|
if not body.first
|
@@ -156,24 +183,46 @@ module Ruby2JS
|
|
156
183
|
end
|
157
184
|
end
|
158
185
|
|
159
|
-
|
186
|
+
# collect instance methods (including getters and setters)
|
160
187
|
@react_props = []
|
161
188
|
@react_methods = []
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
189
|
+
body.each do |statement|
|
190
|
+
if statement.type == :def
|
191
|
+
method = statement.children.first
|
192
|
+
unless method == :initialize
|
193
|
+
if method.to_s.end_with? '='
|
194
|
+
method = method.to_s[0..-2].to_sym
|
195
|
+
@react_props << method unless @react_props.include? method
|
196
|
+
elsif statement.is_method?
|
197
|
+
@react_methods << method unless @react_methods.include? method
|
198
|
+
else
|
199
|
+
@react_props << method unless @react_props.include? method
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# determine which instance methods need binding
|
206
|
+
needs_binding = []
|
207
|
+
scan_events = lambda do |list|
|
208
|
+
list.each do |node|
|
209
|
+
next unless Parser::AST::Node === node
|
210
|
+
node = process node if node.type == :xstr
|
211
|
+
if node.type == :hash
|
212
|
+
node.children.each do |pair|
|
213
|
+
value = pair.children.last
|
214
|
+
if value.type == :send and \
|
215
|
+
@react_methods.include? value.children[1] and \
|
216
|
+
[nil, s(:self), s(:send, nil, :this)].include? value.children[0]
|
217
|
+
|
218
|
+
needs_binding << value.children[1]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
scan_events[node.children]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
scan_events[body] unless es2015
|
177
226
|
|
178
227
|
# append statics (if any)
|
179
228
|
unless statics.empty?
|
@@ -181,9 +230,9 @@ module Ruby2JS
|
|
181
230
|
end
|
182
231
|
|
183
232
|
# create a default getInitialState method if there is no such method
|
184
|
-
# and there are references to instance variables
|
233
|
+
# and there are either references to instance variables or there are
|
234
|
+
# methods that need to be bound.
|
185
235
|
if \
|
186
|
-
not es2015 and
|
187
236
|
not body.any? do |child|
|
188
237
|
child.type == :def and
|
189
238
|
[:getInitialState, :initialize].include? child.children.first
|
@@ -191,9 +240,11 @@ module Ruby2JS
|
|
191
240
|
then
|
192
241
|
@reactIvars = {pre: [], post: [], asgn: [], ref: [], cond: []}
|
193
242
|
react_walk(node)
|
194
|
-
|
243
|
+
if not es2015 and not @reactIvars.values.flatten.empty?
|
195
244
|
body = [s(:def, :getInitialState, s(:args),
|
196
245
|
s(:return, s(:hash))), *body]
|
246
|
+
elsif not needs_binding.empty? or not @reactIvars.values.flatten.empty?
|
247
|
+
body = [s(:def, :initialize, s(:args)), *body]
|
197
248
|
end
|
198
249
|
end
|
199
250
|
|
@@ -221,9 +272,22 @@ module Ruby2JS
|
|
221
272
|
end
|
222
273
|
end
|
223
274
|
|
275
|
+
# add props argument if there is a reference to a prop
|
276
|
+
if args.children.length == 0
|
277
|
+
has_cvar = lambda {|list|
|
278
|
+
list.any? {|node|
|
279
|
+
next unless Parser::AST::Node === node
|
280
|
+
return true if node.type == :cvar
|
281
|
+
has_cvar.call(node.children)
|
282
|
+
}
|
283
|
+
}
|
284
|
+
args = s(:args, s(:arg, 'prop$')) if has_cvar[block]
|
285
|
+
end
|
286
|
+
|
224
287
|
# peel off the initial set of instance variable assignment stmts
|
225
288
|
assigns = []
|
226
289
|
block = block.dup
|
290
|
+
block.shift if block.first == s(:zsuper)
|
227
291
|
while not block.empty? and block.first.type == :ivasgn
|
228
292
|
node = block.shift
|
229
293
|
vars = [node.children.first]
|
@@ -240,9 +304,15 @@ module Ruby2JS
|
|
240
304
|
state = s(:hash, *assigns.map {|anode| s(:pair, s(:str,
|
241
305
|
anode.children.first.to_s[1..-1]), anode.children.last)})
|
242
306
|
|
307
|
+
# bind methods as needed
|
308
|
+
needs_binding.each do |method|
|
309
|
+
block.push(s(:send, s(:self), "#{method}=",
|
310
|
+
s(:send, s(:attr, s(:self), method), :bind, s(:self))))
|
311
|
+
end
|
312
|
+
|
243
313
|
# modify block to build and/or return state
|
244
314
|
if mname == :initialize
|
245
|
-
block.unshift(s(:send, s(:self), :state=, state))
|
315
|
+
block.unshift(s(:zsuper), s(:send, s(:self), :state=, state))
|
246
316
|
elsif block.empty?
|
247
317
|
block = [s(:return, state)]
|
248
318
|
else
|
@@ -250,10 +320,10 @@ module Ruby2JS
|
|
250
320
|
block.push(s(:return, s(:attr, s(:self), :state)))
|
251
321
|
end
|
252
322
|
|
253
|
-
elsif mname == :render
|
323
|
+
elsif mname == :render and not react_wunderbar_free(block, true)
|
254
324
|
if \
|
255
325
|
block.length != 1 or not block.last or
|
256
|
-
not [
|
326
|
+
not %i[send block xstr].include? block.last.type
|
257
327
|
then
|
258
328
|
if @jsx
|
259
329
|
while block.length == 1 and block.first.type == :begin
|
@@ -271,9 +341,9 @@ module Ruby2JS
|
|
271
341
|
block = [*prolog, s(:return,
|
272
342
|
s(:xnode, '', *process_all(block)))]
|
273
343
|
else
|
274
|
-
# wrap multi-line blocks with a
|
344
|
+
# wrap multi-line blocks with a React Fragment
|
275
345
|
block = [s(:return,
|
276
|
-
s(:block, s(:send, nil,
|
346
|
+
s(:block, s(:send, nil, ReactFragment), s(:args), *block))]
|
277
347
|
end
|
278
348
|
end
|
279
349
|
|
@@ -300,7 +370,10 @@ module Ruby2JS
|
|
300
370
|
end
|
301
371
|
|
302
372
|
if es2015
|
303
|
-
pairs <<
|
373
|
+
pairs << child.updated(
|
374
|
+
ReactLifecycle.include?(mname.to_s) ? :defm : :def,
|
375
|
+
[mname, args, process(s(type, *block))]
|
376
|
+
)
|
304
377
|
else
|
305
378
|
pairs << s(:pair, s(:sym, mname), child.updated(:block,
|
306
379
|
[s(:send, nil, :proc), args, process(s(type, *block))]))
|
@@ -369,8 +442,12 @@ module Ruby2JS
|
|
369
442
|
# enable React filtering within React class method calls or
|
370
443
|
# React component calls
|
371
444
|
if \
|
372
|
-
node.children.first == s(:const, nil, :React)
|
445
|
+
node.children.first == s(:const, nil, :React) or
|
446
|
+
node.children.first == s(:const, nil, :ReactDOM)
|
373
447
|
then
|
448
|
+
if @react_imports
|
449
|
+
prepend_list << REACT_IMPORTS[node.children.first.children.last]
|
450
|
+
end
|
374
451
|
|
375
452
|
begin
|
376
453
|
react, @react = @react, true
|
@@ -429,6 +506,10 @@ module Ruby2JS
|
|
429
506
|
# :block arguments are inserted by on_block logic below
|
430
507
|
block = child
|
431
508
|
|
509
|
+
elsif child.type == :splat
|
510
|
+
# arrays need not be expanded
|
511
|
+
text = child.children.first
|
512
|
+
|
432
513
|
else
|
433
514
|
# everything else added as text
|
434
515
|
text = child
|
@@ -586,6 +667,9 @@ module Ruby2JS
|
|
586
667
|
next true if arg.children[1] == :createElement and
|
587
668
|
arg.children[0] == s(:const, nil, :Vue)
|
588
669
|
|
670
|
+
# JSX
|
671
|
+
next true if arg.type == :xstr
|
672
|
+
|
589
673
|
# wunderbar style call
|
590
674
|
arg = arg.children.first if arg.type == :block
|
591
675
|
while arg.type == :send and arg.children.first != nil
|
@@ -598,7 +682,19 @@ module Ruby2JS
|
|
598
682
|
if simple
|
599
683
|
# in the normal case, process each argument
|
600
684
|
reactApply, @reactApply = @reactApply, false
|
601
|
-
|
685
|
+
args.each do |arg|
|
686
|
+
arg = process(arg)
|
687
|
+
if arg.type == :send and
|
688
|
+
arg.children[0] == s(:const, nil, :React) and
|
689
|
+
arg.children[1] == :createElement and
|
690
|
+
arg.children[2] == s(:const, nil, "React.Fragment") and
|
691
|
+
arg.children[3] == s(:nil)
|
692
|
+
then
|
693
|
+
params += arg.children[4..-1]
|
694
|
+
else
|
695
|
+
params << arg
|
696
|
+
end
|
697
|
+
end
|
602
698
|
else
|
603
699
|
reactApply, @reactApply = @reactApply, true
|
604
700
|
|
@@ -860,7 +956,15 @@ module Ruby2JS
|
|
860
956
|
end
|
861
957
|
|
862
958
|
# wunderbar style calls
|
863
|
-
if
|
959
|
+
if child.children[0] == nil and child.children[1] == :_ and \
|
960
|
+
node.children[1].children.empty? and !@jsx
|
961
|
+
|
962
|
+
block = s(:block, s(:send, nil, :proc), s(:args),
|
963
|
+
*node.children[2..-1])
|
964
|
+
return on_send node.children.first.updated(:send,
|
965
|
+
[nil, ReactFragment, block])
|
966
|
+
|
967
|
+
elsif !@jsx and child.children[0] == nil and child.children[1] =~ /^_\w/
|
864
968
|
if node.children[1].children.empty?
|
865
969
|
# append block as a standalone proc
|
866
970
|
block = s(:block, s(:send, nil, :proc), s(:args),
|
@@ -871,9 +975,18 @@ module Ruby2JS
|
|
871
975
|
# iterate over Enumerable arguments if there are args present
|
872
976
|
send = node.children.first.children
|
873
977
|
return super if send.length < 3
|
874
|
-
|
875
|
-
|
876
|
-
|
978
|
+
if node.children.length == 3 and
|
979
|
+
node.children.last.respond_to? :type and
|
980
|
+
node.children.last.type == :send
|
981
|
+
|
982
|
+
return process s(:send, *send[0..1], *send[3..-1],
|
983
|
+
s(:splat, s(:block, s(:send, send[2], :map),
|
984
|
+
node.children[1], s(:return, node.children[2]))))
|
985
|
+
else
|
986
|
+
return process s(:block, s(:send, *send[0..1], *send[3..-1]),
|
987
|
+
s(:args), s(:block, s(:send, send[2], :forEach),
|
988
|
+
*node.children[1..-1]))
|
989
|
+
end
|
877
990
|
end
|
878
991
|
end
|
879
992
|
|
@@ -885,6 +998,13 @@ module Ruby2JS
|
|
885
998
|
end
|
886
999
|
end
|
887
1000
|
|
1001
|
+
def on_lvasgn(node)
|
1002
|
+
return super unless @reactClass
|
1003
|
+
return super unless @react_props.include? node.children.first
|
1004
|
+
node.updated(:send, [s(:self), "#{node.children.first}=",
|
1005
|
+
node.children.last])
|
1006
|
+
end
|
1007
|
+
|
888
1008
|
# convert global variables to refs
|
889
1009
|
def on_gvar(node)
|
890
1010
|
return super unless @reactClass
|
@@ -993,7 +1113,7 @@ module Ruby2JS
|
|
993
1113
|
end
|
994
1114
|
|
995
1115
|
# is this a "wunderbar" style call or createElement?
|
996
|
-
def react_element?(node)
|
1116
|
+
def react_element?(node, wunderbar_only=false)
|
997
1117
|
return false unless node
|
998
1118
|
|
999
1119
|
forEach = [:forEach]
|
@@ -1001,15 +1121,17 @@ module Ruby2JS
|
|
1001
1121
|
|
1002
1122
|
return true if node.type == :block and
|
1003
1123
|
forEach.include? node.children.first.children.last and
|
1004
|
-
react_element?(node.children.last)
|
1124
|
+
react_element?(node.children.last, wunderbar_only)
|
1005
1125
|
|
1006
|
-
|
1007
|
-
|
1008
|
-
node.children[
|
1126
|
+
unless wunderbar_only
|
1127
|
+
# explicit call to React.createElement
|
1128
|
+
return true if node.children[1] == :createElement and
|
1129
|
+
node.children[0] == s(:const, nil, :React)
|
1009
1130
|
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1131
|
+
# explicit call to Vue.createElement
|
1132
|
+
return true if node.children[1] == :createElement and
|
1133
|
+
node.children[0] == s(:const, nil, :Vue)
|
1134
|
+
end
|
1013
1135
|
|
1014
1136
|
# wunderbar style call
|
1015
1137
|
node = node.children.first if node.type == :block
|
@@ -1021,13 +1143,14 @@ module Ruby2JS
|
|
1021
1143
|
|
1022
1144
|
# ensure that there are no "wunderbar" or "createElement" calls in
|
1023
1145
|
# a set of statements.
|
1024
|
-
def react_wunderbar_free(nodes)
|
1146
|
+
def react_wunderbar_free(nodes, wunderbar_only=false)
|
1025
1147
|
nodes.each do |node|
|
1026
1148
|
if Parser::AST::Node === node
|
1027
|
-
return false if
|
1149
|
+
return false if node.type == :xstr
|
1150
|
+
return false if react_element?(node, wunderbar_only)
|
1028
1151
|
|
1029
1152
|
# recurse
|
1030
|
-
return false unless react_wunderbar_free(node.children)
|
1153
|
+
return false unless react_wunderbar_free(node.children, wunderbar_only)
|
1031
1154
|
end
|
1032
1155
|
end
|
1033
1156
|
|
@@ -1181,6 +1304,18 @@ module Ruby2JS
|
|
1181
1304
|
|
1182
1305
|
block
|
1183
1306
|
end
|
1307
|
+
|
1308
|
+
def on_xstr(node)
|
1309
|
+
loc = node.loc
|
1310
|
+
return super unless loc
|
1311
|
+
source = loc.begin.source_buffer.source
|
1312
|
+
source = source[loc.begin.end_pos...loc.end.begin_pos].strip
|
1313
|
+
return super unless @reactClass or source.start_with? '<'
|
1314
|
+
source = Ruby2JS.jsx2_rb(source)
|
1315
|
+
ast = Ruby2JS.parse(source).first
|
1316
|
+
ast = s(:block, s(:send, nil, :_), s(:args), ast) if ast.type == :begin
|
1317
|
+
process ast
|
1318
|
+
end
|
1184
1319
|
end
|
1185
1320
|
|
1186
1321
|
DEFAULTS.push React
|