ruby2js 3.5.3 → 4.0.2
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 +60 -15
- data/lib/ruby2js/converter.rb +39 -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/for.rb +1 -1
- 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/next.rb +10 -2
- data/lib/ruby2js/converter/opasgn.rb +8 -0
- data/lib/ruby2js/converter/redo.rb +14 -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/while.rb +1 -1
- data/lib/ruby2js/converter/whilepost.rb +1 -1
- 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 +137 -109
- 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 +182 -57
- data/lib/ruby2js/filter/require.rb +102 -11
- data/lib/ruby2js/filter/return.rb +13 -1
- data/lib/ruby2js/filter/stimulus.rb +187 -0
- data/lib/ruby2js/jsx.rb +309 -0
- data/lib/ruby2js/namespace.rb +75 -0
- data/lib/ruby2js/serializer.rb +19 -12
- data/lib/ruby2js/sprockets.rb +40 -0
- data/lib/ruby2js/version.rb +3 -3
- data/ruby2js.gemspec +2 -2
- metadata +23 -13
- data/lib/ruby2js/filter/esm_migration.rb +0 -72
- data/lib/ruby2js/rails.rb +0 -63
@@ -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
|
@@ -88,8 +102,8 @@ module Ruby2JS
|
|
88
102
|
end
|
89
103
|
|
90
104
|
if \
|
91
|
-
defined? Ruby2JS::Filter::
|
92
|
-
filters.include? Ruby2JS::Filter::
|
105
|
+
defined? Ruby2JS::Filter::JSX and
|
106
|
+
filters.include? Ruby2JS::Filter::JSX
|
93
107
|
then
|
94
108
|
@jsx = true
|
95
109
|
end
|
@@ -106,8 +120,11 @@ module Ruby2JS
|
|
106
120
|
return super unless cname.children.first == nil
|
107
121
|
return super unless inheritance == s(:const, nil, :React) or
|
108
122
|
inheritance == s(:const, nil, :Vue) or
|
123
|
+
inheritance == s(:const, s(:const, nil, :React), :Component) or
|
109
124
|
inheritance == s(:send, s(:const, nil, :React), :Component)
|
110
125
|
|
126
|
+
prepend_list << REACT_IMPORTS[:React] if modules_enabled?
|
127
|
+
|
111
128
|
# traverse down to actual list of class statements
|
112
129
|
if body.length == 1
|
113
130
|
if not body.first
|
@@ -156,24 +173,46 @@ module Ruby2JS
|
|
156
173
|
end
|
157
174
|
end
|
158
175
|
|
159
|
-
|
176
|
+
# collect instance methods (including getters and setters)
|
160
177
|
@react_props = []
|
161
178
|
@react_methods = []
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
179
|
+
body.each do |statement|
|
180
|
+
if statement.type == :def
|
181
|
+
method = statement.children.first
|
182
|
+
unless method == :initialize
|
183
|
+
if method.to_s.end_with? '='
|
184
|
+
method = method.to_s[0..-2].to_sym
|
185
|
+
@react_props << method unless @react_props.include? method
|
186
|
+
elsif statement.is_method?
|
187
|
+
@react_methods << method unless @react_methods.include? method
|
188
|
+
else
|
189
|
+
@react_props << method unless @react_props.include? method
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# determine which instance methods need binding
|
196
|
+
needs_binding = []
|
197
|
+
scan_events = lambda do |list|
|
198
|
+
list.each do |node|
|
199
|
+
next unless Parser::AST::Node === node
|
200
|
+
node = process node if node.type == :xstr
|
201
|
+
if node.type == :hash
|
202
|
+
node.children.each do |pair|
|
203
|
+
value = pair.children.last
|
204
|
+
if value.type == :send and \
|
205
|
+
@react_methods.include? value.children[1] and \
|
206
|
+
[nil, s(:self), s(:send, nil, :this)].include? value.children[0]
|
207
|
+
|
208
|
+
needs_binding << value.children[1]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
scan_events[node.children]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
scan_events[body] unless es2015
|
177
216
|
|
178
217
|
# append statics (if any)
|
179
218
|
unless statics.empty?
|
@@ -181,9 +220,9 @@ module Ruby2JS
|
|
181
220
|
end
|
182
221
|
|
183
222
|
# create a default getInitialState method if there is no such method
|
184
|
-
# and there are references to instance variables
|
223
|
+
# and there are either references to instance variables or there are
|
224
|
+
# methods that need to be bound.
|
185
225
|
if \
|
186
|
-
not es2015 and
|
187
226
|
not body.any? do |child|
|
188
227
|
child.type == :def and
|
189
228
|
[:getInitialState, :initialize].include? child.children.first
|
@@ -191,9 +230,11 @@ module Ruby2JS
|
|
191
230
|
then
|
192
231
|
@reactIvars = {pre: [], post: [], asgn: [], ref: [], cond: []}
|
193
232
|
react_walk(node)
|
194
|
-
|
233
|
+
if not es2015 and not @reactIvars.values.flatten.empty?
|
195
234
|
body = [s(:def, :getInitialState, s(:args),
|
196
235
|
s(:return, s(:hash))), *body]
|
236
|
+
elsif not needs_binding.empty? or not @reactIvars.values.flatten.empty?
|
237
|
+
body = [s(:def, :initialize, s(:args)), *body]
|
197
238
|
end
|
198
239
|
end
|
199
240
|
|
@@ -221,9 +262,22 @@ module Ruby2JS
|
|
221
262
|
end
|
222
263
|
end
|
223
264
|
|
265
|
+
# add props argument if there is a reference to a prop
|
266
|
+
if args.children.length == 0
|
267
|
+
has_cvar = lambda {|list|
|
268
|
+
list.any? {|node|
|
269
|
+
next unless Parser::AST::Node === node
|
270
|
+
return true if node.type == :cvar
|
271
|
+
has_cvar.call(node.children)
|
272
|
+
}
|
273
|
+
}
|
274
|
+
args = s(:args, s(:arg, 'prop$')) if has_cvar[block]
|
275
|
+
end
|
276
|
+
|
224
277
|
# peel off the initial set of instance variable assignment stmts
|
225
278
|
assigns = []
|
226
279
|
block = block.dup
|
280
|
+
block.shift if block.first == s(:zsuper)
|
227
281
|
while not block.empty? and block.first.type == :ivasgn
|
228
282
|
node = block.shift
|
229
283
|
vars = [node.children.first]
|
@@ -240,9 +294,15 @@ module Ruby2JS
|
|
240
294
|
state = s(:hash, *assigns.map {|anode| s(:pair, s(:str,
|
241
295
|
anode.children.first.to_s[1..-1]), anode.children.last)})
|
242
296
|
|
297
|
+
# bind methods as needed
|
298
|
+
needs_binding.each do |method|
|
299
|
+
block.push(s(:send, s(:self), "#{method}=",
|
300
|
+
s(:send, s(:attr, s(:self), method), :bind, s(:self))))
|
301
|
+
end
|
302
|
+
|
243
303
|
# modify block to build and/or return state
|
244
304
|
if mname == :initialize
|
245
|
-
block.unshift(s(:send, s(:self), :state=, state))
|
305
|
+
block.unshift(s(:zsuper), s(:send, s(:self), :state=, state))
|
246
306
|
elsif block.empty?
|
247
307
|
block = [s(:return, state)]
|
248
308
|
else
|
@@ -250,10 +310,10 @@ module Ruby2JS
|
|
250
310
|
block.push(s(:return, s(:attr, s(:self), :state)))
|
251
311
|
end
|
252
312
|
|
253
|
-
elsif mname == :render
|
313
|
+
elsif mname == :render and not react_wunderbar_free(block, true)
|
254
314
|
if \
|
255
315
|
block.length != 1 or not block.last or
|
256
|
-
not [
|
316
|
+
not %i[send block xstr].include? block.last.type
|
257
317
|
then
|
258
318
|
if @jsx
|
259
319
|
while block.length == 1 and block.first.type == :begin
|
@@ -271,9 +331,9 @@ module Ruby2JS
|
|
271
331
|
block = [*prolog, s(:return,
|
272
332
|
s(:xnode, '', *process_all(block)))]
|
273
333
|
else
|
274
|
-
# wrap multi-line blocks with a
|
334
|
+
# wrap multi-line blocks with a React Fragment
|
275
335
|
block = [s(:return,
|
276
|
-
s(:block, s(:send, nil,
|
336
|
+
s(:block, s(:send, nil, ReactFragment), s(:args), *block))]
|
277
337
|
end
|
278
338
|
end
|
279
339
|
|
@@ -300,7 +360,10 @@ module Ruby2JS
|
|
300
360
|
end
|
301
361
|
|
302
362
|
if es2015
|
303
|
-
pairs <<
|
363
|
+
pairs << child.updated(
|
364
|
+
ReactLifecycle.include?(mname.to_s) ? :defm : :def,
|
365
|
+
[mname, args, process(s(type, *block))]
|
366
|
+
)
|
304
367
|
else
|
305
368
|
pairs << s(:pair, s(:sym, mname), child.updated(:block,
|
306
369
|
[s(:send, nil, :proc), args, process(s(type, *block))]))
|
@@ -369,8 +432,12 @@ module Ruby2JS
|
|
369
432
|
# enable React filtering within React class method calls or
|
370
433
|
# React component calls
|
371
434
|
if \
|
372
|
-
node.children.first == s(:const, nil, :React)
|
435
|
+
node.children.first == s(:const, nil, :React) or
|
436
|
+
node.children.first == s(:const, nil, :ReactDOM)
|
373
437
|
then
|
438
|
+
if modules_enabled?
|
439
|
+
prepend_list << REACT_IMPORTS[node.children.first.children.last]
|
440
|
+
end
|
374
441
|
|
375
442
|
begin
|
376
443
|
react, @react = @react, true
|
@@ -429,6 +496,10 @@ module Ruby2JS
|
|
429
496
|
# :block arguments are inserted by on_block logic below
|
430
497
|
block = child
|
431
498
|
|
499
|
+
elsif child.type == :splat
|
500
|
+
# arrays need not be expanded
|
501
|
+
text = child.children.first
|
502
|
+
|
432
503
|
else
|
433
504
|
# everything else added as text
|
434
505
|
text = child
|
@@ -586,6 +657,9 @@ module Ruby2JS
|
|
586
657
|
next true if arg.children[1] == :createElement and
|
587
658
|
arg.children[0] == s(:const, nil, :Vue)
|
588
659
|
|
660
|
+
# JSX
|
661
|
+
next true if arg.type == :xstr
|
662
|
+
|
589
663
|
# wunderbar style call
|
590
664
|
arg = arg.children.first if arg.type == :block
|
591
665
|
while arg.type == :send and arg.children.first != nil
|
@@ -598,7 +672,19 @@ module Ruby2JS
|
|
598
672
|
if simple
|
599
673
|
# in the normal case, process each argument
|
600
674
|
reactApply, @reactApply = @reactApply, false
|
601
|
-
|
675
|
+
args.each do |arg|
|
676
|
+
arg = process(arg)
|
677
|
+
if arg.type == :send and
|
678
|
+
arg.children[0] == s(:const, nil, :React) and
|
679
|
+
arg.children[1] == :createElement and
|
680
|
+
arg.children[2] == s(:const, nil, "React.Fragment") and
|
681
|
+
arg.children[3] == s(:nil)
|
682
|
+
then
|
683
|
+
params += arg.children[4..-1]
|
684
|
+
else
|
685
|
+
params << arg
|
686
|
+
end
|
687
|
+
end
|
602
688
|
else
|
603
689
|
reactApply, @reactApply = @reactApply, true
|
604
690
|
|
@@ -860,7 +946,15 @@ module Ruby2JS
|
|
860
946
|
end
|
861
947
|
|
862
948
|
# wunderbar style calls
|
863
|
-
if
|
949
|
+
if child.children[0] == nil and child.children[1] == :_ and \
|
950
|
+
node.children[1].children.empty? and !@jsx
|
951
|
+
|
952
|
+
block = s(:block, s(:send, nil, :proc), s(:args),
|
953
|
+
*node.children[2..-1])
|
954
|
+
return on_send node.children.first.updated(:send,
|
955
|
+
[nil, ReactFragment, block])
|
956
|
+
|
957
|
+
elsif !@jsx and child.children[0] == nil and child.children[1] =~ /^_\w/
|
864
958
|
if node.children[1].children.empty?
|
865
959
|
# append block as a standalone proc
|
866
960
|
block = s(:block, s(:send, nil, :proc), s(:args),
|
@@ -871,9 +965,18 @@ module Ruby2JS
|
|
871
965
|
# iterate over Enumerable arguments if there are args present
|
872
966
|
send = node.children.first.children
|
873
967
|
return super if send.length < 3
|
874
|
-
|
875
|
-
|
876
|
-
|
968
|
+
if node.children.length == 3 and
|
969
|
+
node.children.last.respond_to? :type and
|
970
|
+
node.children.last.type == :send
|
971
|
+
|
972
|
+
return process s(:send, *send[0..1], *send[3..-1],
|
973
|
+
s(:splat, s(:block, s(:send, send[2], :map),
|
974
|
+
node.children[1], s(:return, node.children[2]))))
|
975
|
+
else
|
976
|
+
return process s(:block, s(:send, *send[0..1], *send[3..-1]),
|
977
|
+
s(:args), s(:block, s(:send, send[2], :forEach),
|
978
|
+
*node.children[1..-1]))
|
979
|
+
end
|
877
980
|
end
|
878
981
|
end
|
879
982
|
|
@@ -885,6 +988,13 @@ module Ruby2JS
|
|
885
988
|
end
|
886
989
|
end
|
887
990
|
|
991
|
+
def on_lvasgn(node)
|
992
|
+
return super unless @reactClass
|
993
|
+
return super unless @react_props.include? node.children.first
|
994
|
+
node.updated(:send, [s(:self), "#{node.children.first}=",
|
995
|
+
node.children.last])
|
996
|
+
end
|
997
|
+
|
888
998
|
# convert global variables to refs
|
889
999
|
def on_gvar(node)
|
890
1000
|
return super unless @reactClass
|
@@ -993,7 +1103,7 @@ module Ruby2JS
|
|
993
1103
|
end
|
994
1104
|
|
995
1105
|
# is this a "wunderbar" style call or createElement?
|
996
|
-
def react_element?(node)
|
1106
|
+
def react_element?(node, wunderbar_only=false)
|
997
1107
|
return false unless node
|
998
1108
|
|
999
1109
|
forEach = [:forEach]
|
@@ -1001,15 +1111,17 @@ module Ruby2JS
|
|
1001
1111
|
|
1002
1112
|
return true if node.type == :block and
|
1003
1113
|
forEach.include? node.children.first.children.last and
|
1004
|
-
react_element?(node.children.last)
|
1114
|
+
react_element?(node.children.last, wunderbar_only)
|
1005
1115
|
|
1006
|
-
|
1007
|
-
|
1008
|
-
node.children[
|
1116
|
+
unless wunderbar_only
|
1117
|
+
# explicit call to React.createElement
|
1118
|
+
return true if node.children[1] == :createElement and
|
1119
|
+
node.children[0] == s(:const, nil, :React)
|
1009
1120
|
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1121
|
+
# explicit call to Vue.createElement
|
1122
|
+
return true if node.children[1] == :createElement and
|
1123
|
+
node.children[0] == s(:const, nil, :Vue)
|
1124
|
+
end
|
1013
1125
|
|
1014
1126
|
# wunderbar style call
|
1015
1127
|
node = node.children.first if node.type == :block
|
@@ -1021,13 +1133,14 @@ module Ruby2JS
|
|
1021
1133
|
|
1022
1134
|
# ensure that there are no "wunderbar" or "createElement" calls in
|
1023
1135
|
# a set of statements.
|
1024
|
-
def react_wunderbar_free(nodes)
|
1136
|
+
def react_wunderbar_free(nodes, wunderbar_only=false)
|
1025
1137
|
nodes.each do |node|
|
1026
1138
|
if Parser::AST::Node === node
|
1027
|
-
return false if
|
1139
|
+
return false if node.type == :xstr
|
1140
|
+
return false if react_element?(node, wunderbar_only)
|
1028
1141
|
|
1029
1142
|
# recurse
|
1030
|
-
return false unless react_wunderbar_free(node.children)
|
1143
|
+
return false unless react_wunderbar_free(node.children, wunderbar_only)
|
1031
1144
|
end
|
1032
1145
|
end
|
1033
1146
|
|
@@ -1181,6 +1294,18 @@ module Ruby2JS
|
|
1181
1294
|
|
1182
1295
|
block
|
1183
1296
|
end
|
1297
|
+
|
1298
|
+
def on_xstr(node)
|
1299
|
+
loc = node.loc
|
1300
|
+
return super unless loc
|
1301
|
+
source = loc.begin.source_buffer.source
|
1302
|
+
source = source[loc.begin.end_pos...loc.end.begin_pos].strip
|
1303
|
+
return super unless @reactClass or source.start_with? '<'
|
1304
|
+
source = Ruby2JS.jsx2_rb(source)
|
1305
|
+
ast = Ruby2JS.parse(source).first
|
1306
|
+
ast = s(:block, s(:send, nil, :_), s(:args), ast) if ast.type == :begin
|
1307
|
+
process ast
|
1308
|
+
end
|
1184
1309
|
end
|
1185
1310
|
|
1186
1311
|
DEFAULTS.push React
|