ruby2js 2.0.18 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|