ruby2js 2.0.18 → 2.1.0
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/lib/ruby2js/converter.rb +1 -1
- data/lib/ruby2js/filter/react.rb +1 -2
- data/lib/ruby2js/filter/vue.rb +733 -0
- data/lib/ruby2js/version.rb +2 -2
- data/ruby2js.gemspec +4 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af96d4a5579bf41a109fbf248e6639302113cd01
|
4
|
+
data.tar.gz: 90062d392b1cb821f403b3e40f751fd4bcc3cb85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e03b33dc464a0852d874a4c0c8815d9252c7df7c81d08a6ae4dc178b68db16130c9782512fedeb895937f2b2ade2d6f76a75206324d3680b4ec9cbf05233de3a
|
7
|
+
data.tar.gz: 5ba34dc016fd19d17c0b39541c7127b7a117e2aea6ad9bca920e8d2eeb500bab7fd573d74ca1dd797a3382c70dac519fe8ee0dbe299f10f47407efa7d4ee9cf8
|
data/lib/ruby2js/converter.rb
CHANGED
data/lib/ruby2js/filter/react.rb
CHANGED
@@ -771,8 +771,7 @@ module Ruby2JS
|
|
771
771
|
# convert global variables to refs
|
772
772
|
def on_gvar(node)
|
773
773
|
return super unless @reactClass
|
774
|
-
|
775
|
-
node.children.first.to_s[1..-1])
|
774
|
+
s(:attr, s(:attr, s(:self), :refs), node.children.first.to_s[1..-1])
|
776
775
|
end
|
777
776
|
|
778
777
|
# convert instance variables to state
|
@@ -0,0 +1,733 @@
|
|
1
|
+
require 'ruby2js'
|
2
|
+
|
3
|
+
module Ruby2JS
|
4
|
+
module Filter
|
5
|
+
module Vue
|
6
|
+
include SEXP
|
7
|
+
|
8
|
+
VUE_METHODS = [
|
9
|
+
:delete, :destroy, :emit, :forceUpdate, :mount, :nextTick, :off, :on,
|
10
|
+
:once, :set, :watch
|
11
|
+
]
|
12
|
+
|
13
|
+
def initialize(*args)
|
14
|
+
@vue_h = nil
|
15
|
+
@vue_self = nil
|
16
|
+
@vue_apply = nil
|
17
|
+
@vue_inventory = Hash.new {|h, k| h[k] = []}
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def options=(options)
|
22
|
+
super
|
23
|
+
@vue_h ||= options[:vue_h]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Example conversion
|
27
|
+
# before:
|
28
|
+
# (class (const nil :Foo) (const nil :Vue) nil)
|
29
|
+
# after:
|
30
|
+
# (casgn nil :Foo, (send nil, :Vue, :component, (:str, "foo"),
|
31
|
+
# s(:hash)))
|
32
|
+
def on_class(node)
|
33
|
+
cname, inheritance, *body = node.children
|
34
|
+
return super unless cname.children.first == nil
|
35
|
+
return super unless inheritance == s(:const, nil, :Vue)
|
36
|
+
|
37
|
+
# traverse down to actual list of class statements
|
38
|
+
if body.length == 1
|
39
|
+
if not body.first
|
40
|
+
body = []
|
41
|
+
elsif body.first.type == :begin
|
42
|
+
body = body.first.children
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
hash = []
|
47
|
+
methods = []
|
48
|
+
|
49
|
+
# insert constructor if none present
|
50
|
+
unless body.any? {|statement|
|
51
|
+
statement.type == :def and statement.children.first ==:initialize}
|
52
|
+
then
|
53
|
+
body = body.dup
|
54
|
+
body.unshift s(:def, :initialize, s(:args), nil)
|
55
|
+
end
|
56
|
+
|
57
|
+
@vue_inventory = vue_walk(node)
|
58
|
+
|
59
|
+
# convert body into hash
|
60
|
+
body.each do |statement|
|
61
|
+
|
62
|
+
# named values
|
63
|
+
if statement.type == :send and statement.children.first == nil
|
64
|
+
if [:template, :props].include? statement.children[1]
|
65
|
+
hash << s(:pair, s(:sym, statement.children[1]),
|
66
|
+
statement.children[2])
|
67
|
+
end
|
68
|
+
|
69
|
+
# methods
|
70
|
+
elsif statement.type == :def
|
71
|
+
begin
|
72
|
+
@vue_self = s(:attr, s(:self), :$data)
|
73
|
+
method, args, block = statement.children
|
74
|
+
if method == :render
|
75
|
+
args = s(:args, s(:arg, :$h)) if args.children.empty?
|
76
|
+
|
77
|
+
block = s(:begin, block) unless block and block.type == :begin
|
78
|
+
|
79
|
+
if
|
80
|
+
block.children.length != 1 or not block.children.last or
|
81
|
+
not [:send, :block].include? block.children.first.type
|
82
|
+
then
|
83
|
+
# wrap multi-line blocks with a 'span' element
|
84
|
+
block = s(:return,
|
85
|
+
s(:block, s(:send, nil, :_span), s(:args), *block))
|
86
|
+
end
|
87
|
+
|
88
|
+
@vue_h = args.children.first.children.last
|
89
|
+
elsif method == :initialize
|
90
|
+
method = :data
|
91
|
+
|
92
|
+
# find block
|
93
|
+
if block == nil
|
94
|
+
block = s(:begin)
|
95
|
+
elsif block.type != :begin
|
96
|
+
block = s(:begin, block)
|
97
|
+
end
|
98
|
+
|
99
|
+
simple = block.children.all? {|child| child.type == :ivasgn}
|
100
|
+
|
101
|
+
# not so simple if ivars are being read as well as written
|
102
|
+
if simple
|
103
|
+
block_inventory = vue_walk(block)
|
104
|
+
simple = block_inventory[:ivar].empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
uninitialized = @vue_inventory[:ivar].dup
|
108
|
+
|
109
|
+
block.children.each do |child|
|
110
|
+
if child.type == :ivasgn
|
111
|
+
uninitialized.delete child.children.first
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# convert to a hash
|
116
|
+
if simple
|
117
|
+
# simple case: all statements are ivar assignments
|
118
|
+
pairs = block.children.map do |child|
|
119
|
+
s(:pair, s(:sym, child.children[0].to_s[1..-1]),
|
120
|
+
process(child.children[1]))
|
121
|
+
end
|
122
|
+
|
123
|
+
pairs += uninitialized.map do |symbol|
|
124
|
+
s(:pair, s(:sym, symbol.to_s[1..-1]),
|
125
|
+
s(:attr, nil, :undefined))
|
126
|
+
end
|
127
|
+
|
128
|
+
block = s(:return, s(:hash, *pairs))
|
129
|
+
else
|
130
|
+
# general case: build up a hash incrementally
|
131
|
+
block = s(:begin, s(:gvasgn, :$_,
|
132
|
+
s(:hash, *uninitialized.map {|sym|
|
133
|
+
s(:pair, s(:sym, sym.to_s[1..-1]),
|
134
|
+
s(:attr, nil, :undefined))})),
|
135
|
+
block, s(:return, s(:gvar, :$_)))
|
136
|
+
@vue_self = s(:gvar, :$_)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# add to hash in the appropriate location
|
141
|
+
pair = s(:pair, s(:sym, method),
|
142
|
+
s(:block, s(:send, nil, :lambda), args, process(block)))
|
143
|
+
if %w(data render beforeCreate created beforeMount mounted
|
144
|
+
beforeUpdate updated beforeDestroy destroyed
|
145
|
+
).include? method.to_s
|
146
|
+
then
|
147
|
+
hash << pair
|
148
|
+
else
|
149
|
+
methods << pair
|
150
|
+
end
|
151
|
+
ensure
|
152
|
+
@vue_h = nil
|
153
|
+
@vue_self = nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
unless hash.any? {|pair| pair.children[0].children[0] == :props}
|
159
|
+
unless @vue_inventory[:cvar].empty?
|
160
|
+
hash.unshift s(:pair, s(:sym, :props), s(:array,
|
161
|
+
*@vue_inventory[:cvar].map {|sym| s(:str, sym.to_s[2..-1])}))
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# add methods to hash
|
166
|
+
unless methods.empty?
|
167
|
+
hash << s(:pair, s(:sym, :methods), s(:hash, *methods))
|
168
|
+
end
|
169
|
+
|
170
|
+
# convert class name to camel case
|
171
|
+
camel = cname.children.last.to_s.gsub(/[^\w]/, '-').
|
172
|
+
sub(/^[A-Z]/) {|c| c.downcase}.
|
173
|
+
gsub(/[A-Z]/) {|c| "-#{c.downcase}"}
|
174
|
+
|
175
|
+
# build component
|
176
|
+
s(:casgn, nil, cname.children.last,
|
177
|
+
s(:send, s(:const, nil, :Vue), :component,
|
178
|
+
s(:str, camel), s(:hash, *hash)))
|
179
|
+
end
|
180
|
+
|
181
|
+
# expand 'wunderbar' like method calls
|
182
|
+
def on_send(node)
|
183
|
+
if not @vue_h
|
184
|
+
# enable React filtering within React class method calls or
|
185
|
+
# React component calls
|
186
|
+
if
|
187
|
+
node.children.first == s(:const, nil, :Vue)
|
188
|
+
then
|
189
|
+
begin
|
190
|
+
vue_h, @vue_h = @vue_h, :$h
|
191
|
+
return on_send(node)
|
192
|
+
ensure
|
193
|
+
@vue_h = vue_h
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# map method calls involving i/g/c vars to straight calls
|
199
|
+
#
|
200
|
+
# input:
|
201
|
+
# @x.(a,b,c)
|
202
|
+
# output:
|
203
|
+
# @x(a,b,c)
|
204
|
+
if @vue_self and node.children[1] == :call
|
205
|
+
if [:ivar, :gvar, :cvar].include? node.children.first.type
|
206
|
+
return process(s(:send, node.children.first, nil,
|
207
|
+
*node.children[2..-1]))
|
208
|
+
else
|
209
|
+
return super
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
return super unless @vue_h
|
214
|
+
|
215
|
+
if node.children[0] == nil and node.children[1] =~ /^_\w/
|
216
|
+
tag = node.children[1].to_s[1..-1]
|
217
|
+
hash = Hash.new {|h, k| h[k] = {}}
|
218
|
+
args = []
|
219
|
+
complex_block = []
|
220
|
+
component = (tag =~ /^[A-Z]/)
|
221
|
+
|
222
|
+
node.children[2..-1].each do |attr|
|
223
|
+
if attr.type == :hash
|
224
|
+
# attributes
|
225
|
+
# https://github.com/vuejs/babel-plugin-transform-vue-jsx#difference-from-react-jsx
|
226
|
+
pairs = attr.children.dup
|
227
|
+
|
228
|
+
# extract all class names
|
229
|
+
classes = pairs.find_all do |pair|
|
230
|
+
key = pair.children.first.children.first
|
231
|
+
[:class, 'class', :className, 'className'].include? key
|
232
|
+
end
|
233
|
+
|
234
|
+
# combine all classes into a single value (or expression)
|
235
|
+
if classes.length > 0
|
236
|
+
expr = nil
|
237
|
+
values = classes.map do |pair|
|
238
|
+
if [:sym, :str].include? pair.children.last.type
|
239
|
+
pair.children.last.children.first.to_s
|
240
|
+
else
|
241
|
+
expr = pair.children.last
|
242
|
+
''
|
243
|
+
end
|
244
|
+
end
|
245
|
+
pairs -= classes
|
246
|
+
if expr
|
247
|
+
if values.length > 1
|
248
|
+
while expr.type == :begin and expr.children.length == 1
|
249
|
+
expr = expr.children.first
|
250
|
+
end
|
251
|
+
|
252
|
+
if expr.type == :array
|
253
|
+
hash[:class] = s(:array, *expr.children,
|
254
|
+
*values.join(' ').split(' ').map {|str| s(:str, str)})
|
255
|
+
elsif expr.type == :hash
|
256
|
+
hash[:class] = s(:hash, *expr.children,
|
257
|
+
*values.join(' ').split(' ').
|
258
|
+
map {|str| s(:pair, s(:str, str), s(:true))})
|
259
|
+
else
|
260
|
+
if
|
261
|
+
expr.type == :if and expr.children[1] and
|
262
|
+
expr.children[1].type == :str
|
263
|
+
then
|
264
|
+
left = expr.children[1]
|
265
|
+
right = expr.children[2] || s(:str, '')
|
266
|
+
|
267
|
+
unless right.type == :str
|
268
|
+
right = s(:or, right, s(:str, ''))
|
269
|
+
end
|
270
|
+
|
271
|
+
expr = expr.updated(nil,
|
272
|
+
[expr.children[0], left, right])
|
273
|
+
elsif expr.type != :str
|
274
|
+
expr = s(:or, expr, s(:str, ''))
|
275
|
+
end
|
276
|
+
|
277
|
+
value = s(:send, s(:str, values.join(' ')), :+, expr)
|
278
|
+
pairs.unshift s(:pair, s(:sym, :class), value)
|
279
|
+
end
|
280
|
+
elsif [:hash, :array].include? expr.type
|
281
|
+
hash[:class] = expr
|
282
|
+
else
|
283
|
+
pairs.unshift s(:pair, s(:sym, :class), expr)
|
284
|
+
end
|
285
|
+
else
|
286
|
+
hash[:class] = s(:array,
|
287
|
+
*values.join(' ').split(' ').map {|str| s(:str, str)})
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# search for the presence of a 'style' attribute
|
292
|
+
style = pairs.find_index do |pair|
|
293
|
+
['style', :style].include? pair.children.first.children.first
|
294
|
+
end
|
295
|
+
|
296
|
+
# converts style strings into style hashes
|
297
|
+
if style and pairs[style].children[1].type == :str
|
298
|
+
rules = []
|
299
|
+
value = pairs[style].children[1].children[0]
|
300
|
+
value.split(/;\s+/).each do |prop|
|
301
|
+
prop.strip!
|
302
|
+
next unless prop =~ /^([-a-z]+):\s*(.*)$/
|
303
|
+
name, value = $1, $2
|
304
|
+
name.gsub!(/-[a-z]/) {|str| str[1].upcase}
|
305
|
+
if value =~ /^-?\d+$/
|
306
|
+
rules << s(:pair, s(:str, name), s(:int, value.to_i))
|
307
|
+
elsif value =~ /^-?\d+$\.\d*/
|
308
|
+
rules << s(:pair, s(:str, name), s(:float, value.to_f))
|
309
|
+
else
|
310
|
+
rules << s(:pair, s(:str, name), s(:str, value))
|
311
|
+
end
|
312
|
+
end
|
313
|
+
pairs.delete_at(style)
|
314
|
+
hash[:style] = s(:hash, *rules)
|
315
|
+
end
|
316
|
+
|
317
|
+
# process remaining attributes
|
318
|
+
pairs.each do |pair|
|
319
|
+
name = pair.children[0].children[0].to_s
|
320
|
+
if name =~ /^(nativeOn|on)([A-Z])(.*)/
|
321
|
+
hash[$1]["#{$2.downcase}#$3"] = pair.children[1]
|
322
|
+
elsif component
|
323
|
+
hash[:props][name] = pair.children[1]
|
324
|
+
elsif name =~ /^domProps([A-Z])(.*)/
|
325
|
+
hash[:domProps]["#{$1.downcase}#$2"] = pair.children[1]
|
326
|
+
elsif name == 'style' and pair.children[1].type == :hash
|
327
|
+
hash[:style] = pair.children[1]
|
328
|
+
elsif %w(key ref refInFor slot).include? name
|
329
|
+
hash[name] = pair.children[1]
|
330
|
+
else
|
331
|
+
hash[:attrs][name.to_s.gsub('_', '-')] = pair.children[1]
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
elsif attr.type == :block
|
336
|
+
# traverse down to actual list of nested statements
|
337
|
+
statements = attr.children[2..-1]
|
338
|
+
if statements.length == 1
|
339
|
+
if not statements.first
|
340
|
+
statements = []
|
341
|
+
elsif statements.first.type == :begin
|
342
|
+
statements = statements.first.children
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# check for normal case: only elements and text
|
347
|
+
simple = statements.all? do |arg|
|
348
|
+
# explicit call to Vue.createElement
|
349
|
+
next true if arg.children[1] == :createElement and
|
350
|
+
arg.children[0] == s(:const, nil, :Vue)
|
351
|
+
|
352
|
+
# wunderbar style call
|
353
|
+
arg = arg.children.first if arg.type == :block
|
354
|
+
while arg.type == :send and arg.children.first != nil
|
355
|
+
arg = arg.children.first
|
356
|
+
end
|
357
|
+
arg.type == :send and arg.children[1] =~ /^_/
|
358
|
+
end
|
359
|
+
|
360
|
+
if simple
|
361
|
+
args << s(:array, *statements)
|
362
|
+
else
|
363
|
+
complex_block += statements
|
364
|
+
end
|
365
|
+
|
366
|
+
else
|
367
|
+
# text or child elements
|
368
|
+
args << node.children[2]
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# support controlled form components
|
373
|
+
if %w(input select textarea).include? tag
|
374
|
+
# search for the presence of a 'value' attribute
|
375
|
+
value = hash[:attrs]['value']
|
376
|
+
|
377
|
+
# search for the presence of a 'onChange' attribute
|
378
|
+
onChange = hash['on']['input'] ||
|
379
|
+
hash['on']['change'] ||
|
380
|
+
hash['nativeOn']['input'] ||
|
381
|
+
hash['nativeOn']['change']
|
382
|
+
|
383
|
+
if value and value.type == :ivar and !onChange
|
384
|
+
hash['domProps']['value'] ||= value
|
385
|
+
hash['on']['input'] ||=
|
386
|
+
s(:block, s(:send, nil, :proc), s(:args, s(:arg, :event)),
|
387
|
+
s(:ivasgn, value.children.first,
|
388
|
+
s(:attr, s(:attr, s(:lvar, :event), :target), :value)))
|
389
|
+
hash[:attrs].delete('value')
|
390
|
+
end
|
391
|
+
|
392
|
+
if not value and not onChange and tag == 'input'
|
393
|
+
# search for the presence of a 'checked' attribute
|
394
|
+
checked = hash[:attrs]['checked']
|
395
|
+
|
396
|
+
if checked and checked.type == :ivar
|
397
|
+
hash['domProps']['checked'] ||= checked
|
398
|
+
hash['on']['click'] ||=
|
399
|
+
s(:block, s(:send, nil, :proc), s(:args),
|
400
|
+
s(:ivasgn,checked.children.first,
|
401
|
+
s(:send, checked, :!)))
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# put attributes up front
|
407
|
+
unless hash.empty?
|
408
|
+
pairs = hash.to_a.map do |k1, v1|
|
409
|
+
next if Hash === v1 and v1.empty?
|
410
|
+
s(:pair, s(:str, k1.to_s),
|
411
|
+
if Parser::AST::Node === v1
|
412
|
+
v1
|
413
|
+
else
|
414
|
+
s(:hash, *v1.map {|k2, v2| s(:pair, s(:str, k2.to_s), v2)})
|
415
|
+
end
|
416
|
+
)
|
417
|
+
end
|
418
|
+
args.unshift s(:hash, *pairs.compact)
|
419
|
+
end
|
420
|
+
|
421
|
+
# prepend element name
|
422
|
+
if component
|
423
|
+
args.unshift s(:const, nil, tag)
|
424
|
+
else
|
425
|
+
args.unshift s(:str, tag)
|
426
|
+
end
|
427
|
+
|
428
|
+
begin
|
429
|
+
vue_apply = @vue_apply
|
430
|
+
|
431
|
+
if complex_block.empty?
|
432
|
+
@vue_apply = false
|
433
|
+
|
434
|
+
# emit $h (createElement) call
|
435
|
+
element = node.updated :send, [nil, @vue_h, *process_all(args)]
|
436
|
+
else
|
437
|
+
# calls to $h (createElement) which contain a block
|
438
|
+
#
|
439
|
+
# collect array of child elements in a proc, and call that proc
|
440
|
+
#
|
441
|
+
# $h('tag', hash, proc {
|
442
|
+
# var $_ = []
|
443
|
+
# $_.push($h(...))
|
444
|
+
# return $_
|
445
|
+
# }())
|
446
|
+
#
|
447
|
+
@vue_apply = true
|
448
|
+
|
449
|
+
element = node.updated :send, [nil, @vue_h,
|
450
|
+
*process_all(args),
|
451
|
+
s(:send, s(:block, s(:send, nil, :proc),
|
452
|
+
s(:args, s(:shadowarg, :$_)), s(:begin,
|
453
|
+
s(:lvasgn, :$_, s(:array)),
|
454
|
+
*process_all(complex_block),
|
455
|
+
s(:return, s(:lvar, :$_)))), :[])]
|
456
|
+
end
|
457
|
+
ensure
|
458
|
+
@vue_apply = vue_apply
|
459
|
+
end
|
460
|
+
|
461
|
+
if @vue_apply
|
462
|
+
# if apply is set, emit code that pushes result
|
463
|
+
s(:send, s(:gvar, :$_), :push, element)
|
464
|
+
else
|
465
|
+
element
|
466
|
+
end
|
467
|
+
|
468
|
+
elsif node.children[0] == nil and node.children[1] == :_
|
469
|
+
# text nodes
|
470
|
+
# https://stackoverflow.com/questions/42414627/create-text-node-with-custom-render-function-in-vue-js
|
471
|
+
text = s(:send, s(:self), :_v, process(node.children[2]))
|
472
|
+
if @vue_apply
|
473
|
+
# if apply is set, emit code that pushes text
|
474
|
+
s(:send, s(:gvar, :$_), :push, text)
|
475
|
+
else
|
476
|
+
# simple/normal case: simply return the text
|
477
|
+
text
|
478
|
+
end
|
479
|
+
|
480
|
+
elsif node.children[0]==s(:send, nil, :_) and node.children[1]==:[]
|
481
|
+
if @vue_apply
|
482
|
+
# if apply is set, emit code that pushes results
|
483
|
+
s(:send, s(:gvar, :$_), :push, *process_all(node.children[2..-1]))
|
484
|
+
elsif node.children.length == 3
|
485
|
+
process(node.children[2])
|
486
|
+
else
|
487
|
+
# simple/normal case: simply return the element
|
488
|
+
s(:splat, s(:array, *process_all(node.children[2..-1])))
|
489
|
+
end
|
490
|
+
|
491
|
+
elsif
|
492
|
+
node.children[1] == :createElement and
|
493
|
+
node.children[0] == s(:const, nil, :Vue)
|
494
|
+
then
|
495
|
+
# explicit calls to Vue.createElement
|
496
|
+
element = node.updated nil, [nil, :$h,
|
497
|
+
*process_all(node.children[2..-1])]
|
498
|
+
|
499
|
+
if @vue_apply
|
500
|
+
# if apply is set, emit code that pushes result
|
501
|
+
s(:send, s(:gvar, :$_), :push, element)
|
502
|
+
else
|
503
|
+
element
|
504
|
+
end
|
505
|
+
|
506
|
+
elsif
|
507
|
+
VUE_METHODS.include? node.children[1] and
|
508
|
+
node.children[0] == s(:const, nil, :Vue)
|
509
|
+
then
|
510
|
+
# vm methods
|
511
|
+
node.updated nil, [s(:self), "$#{node.children[1]}",
|
512
|
+
*process_all(node.children[2..-1])]
|
513
|
+
|
514
|
+
elsif node.children[0] and node.children[0].type == :send
|
515
|
+
# determine if markaby style class and id names are being used
|
516
|
+
child = node
|
517
|
+
test = child.children.first
|
518
|
+
while test and test.type == :send and not test.is_method?
|
519
|
+
child, test = test, test.children.first
|
520
|
+
end
|
521
|
+
|
522
|
+
if child.children[0] == nil and child.children[1] =~ /^_\w/
|
523
|
+
# capture the arguments provided on the current node
|
524
|
+
children = node.children[2..-1]
|
525
|
+
|
526
|
+
# convert method calls to id and class values
|
527
|
+
while node != child
|
528
|
+
if node.children[1] !~ /!$/
|
529
|
+
# convert method name to hash {class: name} pair
|
530
|
+
pair = s(:pair, s(:sym, :class),
|
531
|
+
s(:str, node.children[1].to_s.gsub('_','-')))
|
532
|
+
else
|
533
|
+
# convert method name to hash {id: name} pair
|
534
|
+
pair = s(:pair, s(:sym, :id),
|
535
|
+
s(:str, node.children[1].to_s[0..-2].gsub('_','-')))
|
536
|
+
end
|
537
|
+
|
538
|
+
# if a hash argument is already passed, merge in id value
|
539
|
+
hash = children.find_index {|cnode| cnode.type == :hash}
|
540
|
+
if hash
|
541
|
+
children[hash] = s(:hash, pair, *children[hash].children)
|
542
|
+
else
|
543
|
+
children << s(:hash, pair)
|
544
|
+
end
|
545
|
+
|
546
|
+
# advance to next node
|
547
|
+
node = node.children.first
|
548
|
+
end
|
549
|
+
|
550
|
+
# collapse series of method calls into a single call
|
551
|
+
return process(node.updated(nil, [*node.children[0..1], *children]))
|
552
|
+
|
553
|
+
else
|
554
|
+
super
|
555
|
+
end
|
556
|
+
|
557
|
+
else
|
558
|
+
super
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
# convert blocks to proc arguments
|
563
|
+
def on_block(node)
|
564
|
+
child = node.children.first
|
565
|
+
|
566
|
+
# map Vue.render(el, &block) to Vue.new(el: el, render: block)
|
567
|
+
if
|
568
|
+
child.children[1] == :render and
|
569
|
+
child.children[0] == s(:const, nil, :Vue)
|
570
|
+
then
|
571
|
+
begin
|
572
|
+
arg = node.children[1].children[0] || s(:arg, :$h)
|
573
|
+
vue_h, @vue_h = @vue_h, arg.children.first
|
574
|
+
|
575
|
+
block = node.children[2]
|
576
|
+
block = s(:begin, block) unless block and block.type == :begin
|
577
|
+
|
578
|
+
if
|
579
|
+
block.children.length != 1 or not block.children.last or
|
580
|
+
not [:send, :block].include? block.children.first.type
|
581
|
+
then
|
582
|
+
# wrap multi-line blocks with a 'span' element
|
583
|
+
block = s(:return,
|
584
|
+
s(:block, s(:send, nil, :_span), s(:args), *block))
|
585
|
+
end
|
586
|
+
|
587
|
+
return node.updated :send, [child.children[0], :new,
|
588
|
+
s(:hash, s(:pair, s(:sym, :el), process(child.children[2])),
|
589
|
+
s(:pair, s(:sym, :render), s(:block, s(:send, nil, :lambda),
|
590
|
+
s(:args, s(:arg, @vue_h)), process(block))))]
|
591
|
+
ensure
|
592
|
+
@vue_h = vue_h
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
return super unless @vue_h
|
597
|
+
|
598
|
+
if
|
599
|
+
child.children[1] == :createElement and
|
600
|
+
child.children[0] == s(:const, nil, :Vue)
|
601
|
+
then
|
602
|
+
# block calls to Vue.createElement
|
603
|
+
#
|
604
|
+
# collect array of child elements in a proc, and call that proc
|
605
|
+
#
|
606
|
+
# $h('tag', hash, proc {
|
607
|
+
# var $_ = []
|
608
|
+
# $_.push($h(...))
|
609
|
+
# return $_
|
610
|
+
# }())
|
611
|
+
#
|
612
|
+
begin
|
613
|
+
vue_apply, @vue_apply = @vue_apply, true
|
614
|
+
|
615
|
+
element = node.updated :send, [nil, @vue_h,
|
616
|
+
*child.children[2..-1],
|
617
|
+
s(:send, s(:block, s(:send, nil, :proc),
|
618
|
+
s(:args, s(:shadowarg, :$_)), s(:begin,
|
619
|
+
s(:lvasgn, :$_, s(:array)),
|
620
|
+
process(node.children[2]),
|
621
|
+
s(:return, s(:lvar, :$_)))), :[])]
|
622
|
+
ensure
|
623
|
+
@vue_apply = vue_apply
|
624
|
+
end
|
625
|
+
|
626
|
+
if @vue_apply
|
627
|
+
# if apply is set, emit code that pushes result
|
628
|
+
return s(:send, s(:gvar, :$_), :push, element)
|
629
|
+
else
|
630
|
+
return element
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
# traverse through potential "css proxy" style method calls
|
635
|
+
child = node.children.first
|
636
|
+
test = child.children.first
|
637
|
+
while test and test.type == :send and not test.is_method?
|
638
|
+
child, test = test, test.children.first
|
639
|
+
end
|
640
|
+
|
641
|
+
# wunderbar style calls
|
642
|
+
if child.children[0] == nil and child.children[1] =~ /^_\w/
|
643
|
+
if node.children[1].children.empty?
|
644
|
+
# append block as a standalone proc
|
645
|
+
block = s(:block, s(:send, nil, :proc), s(:args),
|
646
|
+
*node.children[2..-1])
|
647
|
+
return on_send node.children.first.updated(:send,
|
648
|
+
[*node.children.first.children, block])
|
649
|
+
else
|
650
|
+
# iterate over Enumerable arguments if there are args present
|
651
|
+
send = node.children.first.children
|
652
|
+
return super if send.length < 3
|
653
|
+
return process s(:block, s(:send, *send[0..1], *send[3..-1]),
|
654
|
+
s(:args), s(:block, s(:send, send[2], :forEach),
|
655
|
+
*node.children[1..-1]))
|
656
|
+
end
|
657
|
+
else
|
658
|
+
super
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
# expand @@ to self
|
663
|
+
def on_cvar(node)
|
664
|
+
return super unless @vue_self
|
665
|
+
s(:attr, s(:attr, s(:self), :$props), node.children[0].to_s[2..-1])
|
666
|
+
end
|
667
|
+
|
668
|
+
# prevent attempts to assign to Vue properties
|
669
|
+
def on_cvasgn(node)
|
670
|
+
return super unless @vue_self
|
671
|
+
raise NotImplementedError, "setting a Vue property"
|
672
|
+
end
|
673
|
+
|
674
|
+
# expand @ to @vue_self
|
675
|
+
def on_ivar(node)
|
676
|
+
return super unless @vue_self
|
677
|
+
s(:attr, @vue_self, node.children[0].to_s[1..-1])
|
678
|
+
end
|
679
|
+
|
680
|
+
# expand @= to @vue_self.=
|
681
|
+
def on_ivasgn(node)
|
682
|
+
return super unless @vue_self
|
683
|
+
if node.children.length == 1
|
684
|
+
s(:attr, @vue_self, "#{node.children[0].to_s[1..-1]}")
|
685
|
+
else
|
686
|
+
s(:send, @vue_self, "#{node.children[0].to_s[1..-1]}=",
|
687
|
+
process(node.children[1]))
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
def on_op_asgn(node)
|
692
|
+
return super unless @vue_self
|
693
|
+
return super unless node.children.first.type == :ivasgn
|
694
|
+
node.updated nil, [s(:attr, @vue_self,
|
695
|
+
node.children[0].children[0].to_s[1..-1]),
|
696
|
+
node.children[1], process(node.children[2])]
|
697
|
+
end
|
698
|
+
|
699
|
+
# gather ivar and cvar usage
|
700
|
+
def vue_walk(node, inventory = Hash.new {|h, k| h[k] = []})
|
701
|
+
# extract ivars and cvars
|
702
|
+
if [:ivar, :cvar].include? node.type
|
703
|
+
symbol = node.children.first
|
704
|
+
unless inventory[node.type].include? symbol
|
705
|
+
inventory[node.type] << symbol
|
706
|
+
end
|
707
|
+
elsif node.type == :ivasgn
|
708
|
+
symbol = nil
|
709
|
+
symbol = node.children.first if node.children.length == 1
|
710
|
+
if node.children.length == 2
|
711
|
+
value = node.children[-1]
|
712
|
+
symbol = value.children.first if value.type == :ivasgn
|
713
|
+
end
|
714
|
+
|
715
|
+
if symbol
|
716
|
+
unless inventory[:ivar].include? symbol
|
717
|
+
inventory[:ivar] << symbol
|
718
|
+
end
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
# recurse
|
723
|
+
node.children.each do |child|
|
724
|
+
vue_walk(child, inventory) if Parser::AST::Node === child
|
725
|
+
end
|
726
|
+
|
727
|
+
return inventory
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
DEFAULTS.push Vue
|
732
|
+
end
|
733
|
+
end
|
data/lib/ruby2js/version.rb
CHANGED
data/ruby2js.gemspec
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: ruby2js 2.0
|
2
|
+
# stub: ruby2js 2.1.0 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "ruby2js".freeze
|
6
|
-
s.version = "2.0
|
6
|
+
s.version = "2.1.0"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib".freeze]
|
10
10
|
s.authors = ["Sam Ruby".freeze]
|
11
|
-
s.date = "2017-
|
11
|
+
s.date = "2017-09-03"
|
12
12
|
s.description = " The base package maps Ruby syntax to JavaScript semantics.\n Filters may be provided to add Ruby-specific or framework specific\n behavior.\n".freeze
|
13
13
|
s.email = "rubys@intertwingly.net".freeze
|
14
|
-
s.files = ["README.md".freeze, "lib/ruby2js".freeze, "lib/ruby2js.rb".freeze, "lib/ruby2js/cgi.rb".freeze, "lib/ruby2js/converter".freeze, "lib/ruby2js/converter.rb".freeze, "lib/ruby2js/converter/arg.rb".freeze, "lib/ruby2js/converter/args.rb".freeze, "lib/ruby2js/converter/array.rb".freeze, "lib/ruby2js/converter/begin.rb".freeze, "lib/ruby2js/converter/block.rb".freeze, "lib/ruby2js/converter/blockpass.rb".freeze, "lib/ruby2js/converter/boolean.rb".freeze, "lib/ruby2js/converter/break.rb".freeze, "lib/ruby2js/converter/case.rb".freeze, "lib/ruby2js/converter/casgn.rb".freeze, "lib/ruby2js/converter/class.rb".freeze, "lib/ruby2js/converter/const.rb".freeze, "lib/ruby2js/converter/cvar.rb".freeze, "lib/ruby2js/converter/cvasgn.rb".freeze, "lib/ruby2js/converter/def.rb".freeze, "lib/ruby2js/converter/defined.rb".freeze, "lib/ruby2js/converter/defs.rb".freeze, "lib/ruby2js/converter/dstr.rb".freeze, "lib/ruby2js/converter/for.rb".freeze, "lib/ruby2js/converter/hash.rb".freeze, "lib/ruby2js/converter/if.rb".freeze, "lib/ruby2js/converter/in.rb".freeze, "lib/ruby2js/converter/ivar.rb".freeze, "lib/ruby2js/converter/ivasgn.rb".freeze, "lib/ruby2js/converter/kwbegin.rb".freeze, "lib/ruby2js/converter/literal.rb".freeze, "lib/ruby2js/converter/logical.rb".freeze, "lib/ruby2js/converter/masgn.rb".freeze, "lib/ruby2js/converter/module.rb".freeze, "lib/ruby2js/converter/next.rb".freeze, "lib/ruby2js/converter/nil.rb".freeze, "lib/ruby2js/converter/nthref.rb".freeze, "lib/ruby2js/converter/opasgn.rb".freeze, "lib/ruby2js/converter/prototype.rb".freeze, "lib/ruby2js/converter/regexp.rb".freeze, "lib/ruby2js/converter/return.rb".freeze, "lib/ruby2js/converter/self.rb".freeze, "lib/ruby2js/converter/send.rb".freeze, "lib/ruby2js/converter/super.rb".freeze, "lib/ruby2js/converter/sym.rb".freeze, "lib/ruby2js/converter/undef.rb".freeze, "lib/ruby2js/converter/until.rb".freeze, "lib/ruby2js/converter/untilpost.rb".freeze, "lib/ruby2js/converter/var.rb".freeze, "lib/ruby2js/converter/vasgn.rb".freeze, "lib/ruby2js/converter/while.rb".freeze, "lib/ruby2js/converter/whilepost.rb".freeze, "lib/ruby2js/converter/xstr.rb".freeze, "lib/ruby2js/execjs.rb".freeze, "lib/ruby2js/filter".freeze, "lib/ruby2js/filter/angular-resource.rb".freeze, "lib/ruby2js/filter/angular-route.rb".freeze, "lib/ruby2js/filter/angularrb.rb".freeze, "lib/ruby2js/filter/camelCase.rb".freeze, "lib/ruby2js/filter/functions.rb".freeze, "lib/ruby2js/filter/jquery.rb".freeze, "lib/ruby2js/filter/minitest-jasmine.rb".freeze, "lib/ruby2js/filter/react.rb".freeze, "lib/ruby2js/filter/require.rb".freeze, "lib/ruby2js/filter/return.rb".freeze, "lib/ruby2js/filter/rubyjs.rb".freeze, "lib/ruby2js/filter/strict.rb".freeze, "lib/ruby2js/filter/underscore.rb".freeze, "lib/ruby2js/rails.rb".freeze, "lib/ruby2js/serializer.rb".freeze, "lib/ruby2js/sinatra.rb".freeze, "lib/ruby2js/version.rb".freeze, "ruby2js.gemspec".freeze]
|
14
|
+
s.files = ["README.md".freeze, "lib/ruby2js".freeze, "lib/ruby2js.rb".freeze, "lib/ruby2js/cgi.rb".freeze, "lib/ruby2js/converter".freeze, "lib/ruby2js/converter.rb".freeze, "lib/ruby2js/converter/arg.rb".freeze, "lib/ruby2js/converter/args.rb".freeze, "lib/ruby2js/converter/array.rb".freeze, "lib/ruby2js/converter/begin.rb".freeze, "lib/ruby2js/converter/block.rb".freeze, "lib/ruby2js/converter/blockpass.rb".freeze, "lib/ruby2js/converter/boolean.rb".freeze, "lib/ruby2js/converter/break.rb".freeze, "lib/ruby2js/converter/case.rb".freeze, "lib/ruby2js/converter/casgn.rb".freeze, "lib/ruby2js/converter/class.rb".freeze, "lib/ruby2js/converter/const.rb".freeze, "lib/ruby2js/converter/cvar.rb".freeze, "lib/ruby2js/converter/cvasgn.rb".freeze, "lib/ruby2js/converter/def.rb".freeze, "lib/ruby2js/converter/defined.rb".freeze, "lib/ruby2js/converter/defs.rb".freeze, "lib/ruby2js/converter/dstr.rb".freeze, "lib/ruby2js/converter/for.rb".freeze, "lib/ruby2js/converter/hash.rb".freeze, "lib/ruby2js/converter/if.rb".freeze, "lib/ruby2js/converter/in.rb".freeze, "lib/ruby2js/converter/ivar.rb".freeze, "lib/ruby2js/converter/ivasgn.rb".freeze, "lib/ruby2js/converter/kwbegin.rb".freeze, "lib/ruby2js/converter/literal.rb".freeze, "lib/ruby2js/converter/logical.rb".freeze, "lib/ruby2js/converter/masgn.rb".freeze, "lib/ruby2js/converter/module.rb".freeze, "lib/ruby2js/converter/next.rb".freeze, "lib/ruby2js/converter/nil.rb".freeze, "lib/ruby2js/converter/nthref.rb".freeze, "lib/ruby2js/converter/opasgn.rb".freeze, "lib/ruby2js/converter/prototype.rb".freeze, "lib/ruby2js/converter/regexp.rb".freeze, "lib/ruby2js/converter/return.rb".freeze, "lib/ruby2js/converter/self.rb".freeze, "lib/ruby2js/converter/send.rb".freeze, "lib/ruby2js/converter/super.rb".freeze, "lib/ruby2js/converter/sym.rb".freeze, "lib/ruby2js/converter/undef.rb".freeze, "lib/ruby2js/converter/until.rb".freeze, "lib/ruby2js/converter/untilpost.rb".freeze, "lib/ruby2js/converter/var.rb".freeze, "lib/ruby2js/converter/vasgn.rb".freeze, "lib/ruby2js/converter/while.rb".freeze, "lib/ruby2js/converter/whilepost.rb".freeze, "lib/ruby2js/converter/xstr.rb".freeze, "lib/ruby2js/execjs.rb".freeze, "lib/ruby2js/filter".freeze, "lib/ruby2js/filter/angular-resource.rb".freeze, "lib/ruby2js/filter/angular-route.rb".freeze, "lib/ruby2js/filter/angularrb.rb".freeze, "lib/ruby2js/filter/camelCase.rb".freeze, "lib/ruby2js/filter/functions.rb".freeze, "lib/ruby2js/filter/jquery.rb".freeze, "lib/ruby2js/filter/minitest-jasmine.rb".freeze, "lib/ruby2js/filter/react.rb".freeze, "lib/ruby2js/filter/require.rb".freeze, "lib/ruby2js/filter/return.rb".freeze, "lib/ruby2js/filter/rubyjs.rb".freeze, "lib/ruby2js/filter/strict.rb".freeze, "lib/ruby2js/filter/underscore.rb".freeze, "lib/ruby2js/filter/vue.rb".freeze, "lib/ruby2js/rails.rb".freeze, "lib/ruby2js/serializer.rb".freeze, "lib/ruby2js/sinatra.rb".freeze, "lib/ruby2js/version.rb".freeze, "ruby2js.gemspec".freeze]
|
15
15
|
s.homepage = "http://github.com/rubys/ruby2js".freeze
|
16
16
|
s.licenses = ["MIT".freeze]
|
17
17
|
s.required_ruby_version = Gem::Requirement.new(">= 1.9.3".freeze)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby2js
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Ruby
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- lib/ruby2js/filter/rubyjs.rb
|
100
100
|
- lib/ruby2js/filter/strict.rb
|
101
101
|
- lib/ruby2js/filter/underscore.rb
|
102
|
+
- lib/ruby2js/filter/vue.rb
|
102
103
|
- lib/ruby2js/rails.rb
|
103
104
|
- lib/ruby2js/serializer.rb
|
104
105
|
- lib/ruby2js/sinatra.rb
|