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