ruby2js 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +44 -4
  3. data/lib/ruby2js.rb +14 -2
  4. data/lib/ruby2js/converter.rb +11 -3
  5. data/lib/ruby2js/converter/args.rb +1 -1
  6. data/lib/ruby2js/converter/block.rb +2 -2
  7. data/lib/ruby2js/converter/class.rb +2 -2
  8. data/lib/ruby2js/converter/class2.rb +38 -9
  9. data/lib/ruby2js/converter/cvar.rb +1 -1
  10. data/lib/ruby2js/converter/cvasgn.rb +1 -1
  11. data/lib/ruby2js/converter/def.rb +2 -2
  12. data/lib/ruby2js/converter/for.rb +7 -0
  13. data/lib/ruby2js/converter/hash.rb +3 -3
  14. data/lib/ruby2js/converter/if.rb +13 -2
  15. data/lib/ruby2js/converter/import.rb +38 -0
  16. data/lib/ruby2js/converter/logical.rb +46 -1
  17. data/lib/ruby2js/converter/opasgn.rb +5 -2
  18. data/lib/ruby2js/converter/regexp.rb +27 -2
  19. data/lib/ruby2js/converter/return.rb +1 -1
  20. data/lib/ruby2js/converter/send.rb +53 -27
  21. data/lib/ruby2js/converter/super.rb +15 -9
  22. data/lib/ruby2js/converter/xnode.rb +89 -0
  23. data/lib/ruby2js/es2021.rb +5 -0
  24. data/lib/ruby2js/es2021/strict.rb +3 -0
  25. data/lib/ruby2js/filter/cjs.rb +2 -2
  26. data/lib/ruby2js/filter/esm.rb +72 -0
  27. data/lib/ruby2js/filter/functions.rb +142 -26
  28. data/lib/ruby2js/filter/matchAll.rb +49 -0
  29. data/lib/ruby2js/filter/node.rb +18 -10
  30. data/lib/ruby2js/filter/nokogiri.rb +13 -13
  31. data/lib/ruby2js/filter/react.rb +190 -30
  32. data/lib/ruby2js/filter/require.rb +1 -4
  33. data/lib/ruby2js/filter/rubyjs.rb +4 -4
  34. data/lib/ruby2js/filter/vue.rb +15 -15
  35. data/lib/ruby2js/filter/wunderbar.rb +63 -0
  36. data/lib/ruby2js/serializer.rb +25 -11
  37. data/lib/ruby2js/version.rb +1 -1
  38. metadata +10 -3
@@ -36,7 +36,7 @@ module Ruby2JS
36
36
  return super if excluded?(method)
37
37
 
38
38
  if target == nil
39
- if
39
+ if \
40
40
  method == :require and args.length == 1 and
41
41
  args.first.type == :str and
42
42
  %w(nokogiri nokogumbo).include? args.first.children.first
@@ -47,7 +47,7 @@ module Ruby2JS
47
47
  super
48
48
  end
49
49
 
50
- elsif
50
+ elsif \
51
51
  [:HTML, :HTML5].include? method and
52
52
  target == s(:const, nil, :Nokogiri)
53
53
  then
@@ -55,7 +55,7 @@ module Ruby2JS
55
55
  S(:attr, s(:attr, s(:send, s(:const, nil, :JSDOM), :new,
56
56
  *process_all(args)), :window), :document)
57
57
 
58
- elsif
58
+ elsif \
59
59
  method == :parse and
60
60
  target.type == :const and
61
61
  target.children.first == s(:const, nil, :Nokogiri) and
@@ -65,13 +65,13 @@ module Ruby2JS
65
65
  S(:attr, s(:attr, s(:send, s(:const, nil, :JSDOM), :new,
66
66
  *process_all(args)), :window), :document)
67
67
 
68
- elsif
68
+ elsif \
69
69
  method == :at and
70
70
  args.length == 1 and args.first.type == :str
71
71
  then
72
72
  S(:send, target, :querySelector, process(args.first))
73
73
 
74
- elsif
74
+ elsif \
75
75
  method == :search and
76
76
  args.length == 1 and args.first.type == :str
77
77
  then
@@ -98,25 +98,25 @@ module Ruby2JS
98
98
  elsif method === :to_html and args.length == 0
99
99
  S(:attr, target, :outerHTML)
100
100
 
101
- elsif
101
+ elsif \
102
102
  [:attr, :get_attribute].include? method and
103
103
  args.length == 1 and args.first.type == :str
104
104
  then
105
105
  S(:send, target, :getAttribute, process(args.first))
106
106
 
107
- elsif
107
+ elsif \
108
108
  [:key?, :has_attribute].include? method and
109
109
  args.length == 1 and args.first.type == :str
110
110
  then
111
111
  S(:send, target, :hasAttribute, process(args.first))
112
112
 
113
- elsif
113
+ elsif \
114
114
  method == :set_attribute and
115
115
  args.length == 2 and args.first.type == :str
116
116
  then
117
117
  S(:send, target, :setAttribute, *process_all(args))
118
118
 
119
- elsif
119
+ elsif \
120
120
  method == :attribute and
121
121
  args.length == 1 and args.first.type == :str
122
122
  then
@@ -128,7 +128,7 @@ module Ruby2JS
128
128
  elsif method == :attribute_nodes and args.length == 0
129
129
  S(:attr, target, :attributes)
130
130
 
131
- elsif
131
+ elsif \
132
132
  method == :new and args.length == 2 and
133
133
  target == s(:const, s(:const, s(:const, nil, :Nokogiri), :XML), :Node)
134
134
  then
@@ -172,14 +172,14 @@ module Ruby2JS
172
172
  elsif method == :add_child and args.length == 1
173
173
  S(:send, target, :appendChild, process(args.first))
174
174
 
175
- elsif
175
+ elsif \
176
176
  [:add_next_sibling, :next=, :after].include? method and
177
177
  args.length == 1
178
178
  then
179
179
  S(:send, s(:attr, target, :parentNode), :insertBefore,
180
180
  process(args.first), s(:attr, target, :nextSibling))
181
181
 
182
- elsif
182
+ elsif \
183
183
  [:add_previous_sibling, :previous=, :before].include? method and
184
184
  args.length == 1
185
185
  then
@@ -199,7 +199,7 @@ module Ruby2JS
199
199
  elsif method == :previous_element and args.length == 0
200
200
  S(:attr, target, :previousElement)
201
201
 
202
- elsif
202
+ elsif \
203
203
  [:previous, :previous_sibling].include? method and args.length == 0
204
204
  then
205
205
  S(:attr, target, :previousSibling)
@@ -68,12 +68,31 @@ module Ruby2JS
68
68
  @reactApply = nil
69
69
  @reactBlock = nil
70
70
  @reactClass = nil
71
+ @react_props = []
72
+ @react_methods = []
73
+ @react_filter_functions = false
74
+ @jsx = false
71
75
  super
72
76
  end
73
77
 
74
78
  def options=(options)
75
79
  super
76
80
  @react = true if options[:react]
81
+ filters = options[:filters] || Filter::DEFAULTS
82
+
83
+ if \
84
+ defined? Ruby2JS::Filter::Functions and
85
+ filters.include? Ruby2JS::Filter::Functions
86
+ then
87
+ @react_filter_functions = true
88
+ end
89
+
90
+ if \
91
+ defined? Ruby2JS::Filter::Wunderbar and
92
+ filters.include? Ruby2JS::Filter::Wunderbar
93
+ then
94
+ @jsx = true
95
+ end
77
96
  end
78
97
 
79
98
  # Example conversion
@@ -85,7 +104,9 @@ module Ruby2JS
85
104
  def on_class(node)
86
105
  cname, inheritance, *body = node.children
87
106
  return super unless cname.children.first == nil
88
- return super unless inheritance == s(:const, nil, :React)
107
+ return super unless inheritance == s(:const, nil, :React) or
108
+ inheritance == s(:const, nil, :Vue) or
109
+ inheritance == s(:send, s(:const, nil, :React), :Component)
89
110
 
90
111
  # traverse down to actual list of class statements
91
112
  if body.length == 1
@@ -106,18 +127,25 @@ module Ruby2JS
106
127
  react, @react = @react, true
107
128
  reactClass, @reactClass = @reactClass, true
108
129
 
109
- # automatically capture the displayName for the class
110
- pairs = [s(:pair, s(:sym, :displayName),
111
- s(:str, cname.children.last.to_s))]
130
+ pairs = []
131
+
132
+ unless es2015
133
+ # automatically capture the displayName for the class
134
+ pairs << s(:pair, s(:sym, :displayName),
135
+ s(:str, cname.children.last.to_s))
136
+ end
112
137
 
113
138
  # collect static properties/functions
114
139
  statics = []
115
140
  body.select {|child| child.type == :defs}.each do |child|
116
141
  _parent, mname, args, *block = child.children
117
- if child.is_method?
142
+ if es2015
143
+ block = [s(:autoreturn, *block)] unless child.is_method?
144
+ pairs << s(:defs, s(:self), mname, args, *block)
145
+ elsif child.is_method?
118
146
  statics << s(:pair, s(:sym, mname), process(child.updated(:block,
119
147
  [s(:send, nil, :proc), args, s(:autoreturn, *block)])))
120
- elsif
148
+ elsif \
121
149
  block.length == 1 and
122
150
  Converter::EXPRESSIONS.include? block.first.type
123
151
  then
@@ -128,6 +156,25 @@ module Ruby2JS
128
156
  end
129
157
  end
130
158
 
159
+ # collect instance methods (including getters and setters)
160
+ @react_props = []
161
+ @react_methods = []
162
+ body.each do |statement|
163
+ if statement.type == :def
164
+ method = statement.children.first
165
+ unless method == :initialize
166
+ if method.to_s.end_with? '='
167
+ method = method.to_s[0..-2].to_sym
168
+ @react_props << method unless @react_props.include? method
169
+ elsif statement.is_method?
170
+ @react_methods << method unless @react_methods.include? method
171
+ else
172
+ @react_props << method unless @react_props.include? method
173
+ end
174
+ end
175
+ end
176
+ end
177
+
131
178
  # append statics (if any)
132
179
  unless statics.empty?
133
180
  pairs << s(:pair, s(:sym, :statics), s(:hash, *statics))
@@ -135,7 +182,8 @@ module Ruby2JS
135
182
 
136
183
  # create a default getInitialState method if there is no such method
137
184
  # and there are references to instance variables.
138
- if
185
+ if \
186
+ not es2015 and
139
187
  not body.any? do |child|
140
188
  child.type == :def and
141
189
  [:getInitialState, :initialize].include? child.children.first
@@ -162,7 +210,7 @@ module Ruby2JS
162
210
  (@reactIvars[:pre] + @reactIvars[:post]).uniq
163
211
 
164
212
  if mname == :initialize
165
- mname = :getInitialState
213
+ mname = es2015 ? :initialize : :getInitialState
166
214
 
167
215
  # extract real list of statements
168
216
  if block.length == 1
@@ -193,7 +241,9 @@ module Ruby2JS
193
241
  anode.children.first.to_s[1..-1]), anode.children.last)})
194
242
 
195
243
  # modify block to build and/or return state
196
- if block.empty?
244
+ if mname == :initialize
245
+ block.unshift(s(:send, s(:self), :state=, state))
246
+ elsif block.empty?
197
247
  block = [s(:return, state)]
198
248
  else
199
249
  block.unshift(s(:send, s(:self), :state=, state))
@@ -201,13 +251,30 @@ module Ruby2JS
201
251
  end
202
252
 
203
253
  elsif mname == :render
204
- if
205
- block.length != 1 or not block.last or
254
+ if \
255
+ block.length != 1 or not block.last or
206
256
  not [:send, :block].include? block.last.type
207
257
  then
208
- # wrap multi-line blocks with a 'span' element
209
- block = [s(:return,
210
- s(:block, s(:send, nil, :_span), s(:args), *block))]
258
+ if @jsx
259
+ while block.length == 1 and block.first.type == :begin
260
+ block = block.first.children.dup
261
+ end
262
+
263
+ # gather non-element emitting statements in the front
264
+ prolog = []
265
+ while not block.empty? and
266
+ react_wunderbar_free([block.first]) do
267
+ prolog << process(block.shift)
268
+ end
269
+
270
+ # wrap multi-line blocks with an empty element
271
+ block = [*prolog, s(:return,
272
+ s(:xnode, '', *process_all(block)))]
273
+ else
274
+ # wrap multi-line blocks with a 'span' element
275
+ block = [s(:return,
276
+ s(:block, s(:send, nil, :_span), s(:args), *block))]
277
+ end
211
278
  end
212
279
 
213
280
  elsif mname == :componentWillReceiveProps
@@ -227,12 +294,17 @@ module Ruby2JS
227
294
 
228
295
  # add method to class
229
296
  type = (child.is_method? ? :begin : :autoreturn)
297
+ type = :begin if mname == :initialize
230
298
  if block.length == 1 and Parser::AST::Node === block.first
231
299
  type = :begin if block.first.type == :return
232
300
  end
233
301
 
234
- pairs << s(:pair, s(:sym, mname), child.updated(:block,
235
- [s(:send, nil, :proc), args, process(s(type, *block))]))
302
+ if es2015
303
+ pairs << s(:def, mname, args, process(s(type, *block)))
304
+ else
305
+ pairs << s(:pair, s(:sym, mname), child.updated(:block,
306
+ [s(:send, nil, :proc), args, process(s(type, *block))]))
307
+ end
236
308
 
237
309
  # retain comment
238
310
  unless @comments[child].empty?
@@ -245,18 +317,61 @@ module Ruby2JS
245
317
  @reactMethod = nil
246
318
  end
247
319
 
248
- # emit a createClass statement
249
- node.updated(:casgn, [nil, cname.children.last,
250
- s(:send, inheritance, :createClass, s(:hash, *pairs))])
320
+ if es2015
321
+ # emit a class that extends React.Component
322
+ node.updated(:class, [s(:const, nil, cname.children.last),
323
+ s(:attr, s(:const, nil, :React), :Component), *pairs])
324
+ else
325
+ # emit a createClass statement
326
+ node.updated(:casgn, [nil, cname.children.last,
327
+ s(:send, s(:const, nil, :React), :createClass, s(:hash, *pairs))])
328
+ end
251
329
  end
252
330
 
253
331
  def on_send(node)
332
+ # convert Vue.utile.defineReactive to class fields or assignments
333
+ if node.children.first == s(:send, s(:const, nil, :Vue), :util)
334
+ if node.children[1] == :defineReactive
335
+ if node.children[2].type == :cvar
336
+ return process s(:cvasgn, node.children[2].children.first,
337
+ node.children[3])
338
+ elsif node.children[2].type == :send
339
+ assign = node.children[2]
340
+ return assign.updated(nil, [assign.children[0],
341
+ assign.children[1].to_s + '=', node.children[3]])
342
+ end
343
+ end
344
+ end
345
+
346
+ # calls to methods (including getters) defined in this class
347
+ if node.children[0]==nil and Symbol === node.children[1]
348
+ if node.is_method?
349
+ if @react_methods.include? node.children[1]
350
+ # calls to methods defined in this class
351
+ return node.updated nil, [s(:self), node.children[1],
352
+ *process_all(node.children[2..-1])]
353
+ end
354
+ else
355
+ if @react_props.include? node.children[1]
356
+ # access to properties defined in this class
357
+ return node.updated nil, [s(:self), node.children[1],
358
+ *process_all(node.children[2..-1])]
359
+ end
360
+ end
361
+ end
362
+
363
+ if node.children.first == s(:const, nil, :Vue)
364
+ node = node.updated(nil, [s(:const, nil, :React),
365
+ *node.children[1..-1]])
366
+ end
367
+
254
368
  if not @react
255
369
  # enable React filtering within React class method calls or
256
370
  # React component calls
257
- if
371
+ if \
258
372
  node.children.first == s(:const, nil, :React)
259
373
  then
374
+
260
375
  begin
261
376
  react, @react = @react, true
262
377
  return on_send(node)
@@ -278,7 +393,7 @@ module Ruby2JS
278
393
  process(node.children[2])
279
394
  end
280
395
 
281
- elsif
396
+ elsif \
282
397
  @reactApply and node.children[1] == :createElement and
283
398
  node.children[0] == s(:const, nil, :React)
284
399
  then
@@ -286,7 +401,7 @@ module Ruby2JS
286
401
  s(:send, s(:gvar, :$_), :push, s(:send, *node.children[0..1],
287
402
  *process_all(node.children[2..-1])))
288
403
 
289
- elsif node.children[0] == nil and node.children[1] =~ /^_\w/
404
+ elsif !@jsx and node.children[0] == nil and node.children[1] =~ /^_\w/
290
405
  # map method calls starting with an underscore to React calls
291
406
  # to create an element.
292
407
  #
@@ -344,7 +459,7 @@ module Ruby2JS
344
459
  expr = expr.children.first
345
460
  end
346
461
 
347
- if
462
+ if \
348
463
  expr.type == :if and expr.children[1] and
349
464
  expr.children[1].type == :str
350
465
  then
@@ -468,6 +583,8 @@ module Ruby2JS
468
583
  # explicit call to React.createElement
469
584
  next true if arg.children[1] == :createElement and
470
585
  arg.children[0] == s(:const, nil, :React)
586
+ next true if arg.children[1] == :createElement and
587
+ arg.children[0] == s(:const, nil, :Vue)
471
588
 
472
589
  # wunderbar style call
473
590
  arg = arg.children.first if arg.type == :block
@@ -669,7 +786,7 @@ module Ruby2JS
669
786
  super
670
787
  end
671
788
 
672
- elsif
789
+ elsif \
673
790
  node.children[0] and node.children[0].type == :self and
674
791
  node.children.length == 2 and
675
792
  node.children[1] == :componentWillReceiveProps
@@ -686,7 +803,7 @@ module Ruby2JS
686
803
  if not @react
687
804
  # enable React filtering within React class method calls or
688
805
  # React component calls
689
- if
806
+ if \
690
807
  node.children.first == s(:const, nil, :React)
691
808
  then
692
809
  begin
@@ -713,7 +830,7 @@ module Ruby2JS
713
830
  #
714
831
  # Base Ruby2JS processing will convert the 'splat' to 'apply'
715
832
  child = node.children.first
716
- if
833
+ if \
717
834
  child.children[1] == :createElement and
718
835
  child.children[0] == s(:const, nil, :React)
719
836
  then
@@ -743,7 +860,7 @@ module Ruby2JS
743
860
  end
744
861
 
745
862
  # wunderbar style calls
746
- if child.children[0] == nil and child.children[1] =~ /^_\w/
863
+ if !@jsx and child.children[0] == nil and child.children[1] =~ /^_\w/
747
864
  if node.children[1].children.empty?
748
865
  # append block as a standalone proc
749
866
  block = s(:block, s(:send, nil, :proc), s(:args),
@@ -875,6 +992,49 @@ module Ruby2JS
875
992
  s(:attr, @reactProps, node.children.first.to_s[2..-1])
876
993
  end
877
994
 
995
+ # is this a "wunderbar" style call or createElement?
996
+ def react_element?(node)
997
+ return false unless node
998
+
999
+ forEach = [:forEach]
1000
+ forEach << :each if @react_filter_functions
1001
+
1002
+ return true if node.type == :block and
1003
+ forEach.include? node.children.first.children.last and
1004
+ react_element?(node.children.last)
1005
+
1006
+ # explicit call to React.createElement
1007
+ return true if node.children[1] == :createElement and
1008
+ node.children[0] == s(:const, nil, :React)
1009
+
1010
+ # explicit call to Vue.createElement
1011
+ return true if node.children[1] == :createElement and
1012
+ node.children[0] == s(:const, nil, :Vue)
1013
+
1014
+ # wunderbar style call
1015
+ node = node.children.first if node.type == :block
1016
+ while node.type == :send and node.children.first != nil
1017
+ node = node.children.first
1018
+ end
1019
+ node.type == :send and node.children[1].to_s.start_with? '_'
1020
+ end
1021
+
1022
+ # ensure that there are no "wunderbar" or "createElement" calls in
1023
+ # a set of statements.
1024
+ def react_wunderbar_free(nodes)
1025
+ nodes.each do |node|
1026
+ if Parser::AST::Node === node
1027
+ return false if react_element?(node)
1028
+
1029
+ # recurse
1030
+ return false unless react_wunderbar_free(node.children)
1031
+ end
1032
+ end
1033
+
1034
+ # no problems found
1035
+ return true
1036
+ end
1037
+
878
1038
  # analyze ivar usage
879
1039
  def react_walk(node)
880
1040
  # ignore hash values which are blocks (most typically, event handlers)
@@ -920,7 +1080,7 @@ module Ruby2JS
920
1080
  end
921
1081
 
922
1082
  when :send
923
- if
1083
+ if \
924
1084
  child and child.type == :self and node.children.length == 2 and
925
1085
  node.children[1] == :componentWillReceiveProps
926
1086
  then
@@ -933,7 +1093,7 @@ module Ruby2JS
933
1093
  # Ruby 'desugars' -> to lambda, and Ruby2JS presumes that lambdas
934
1094
  # return a value.
935
1095
  def on_pair(node)
936
- if
1096
+ if \
937
1097
  node.children[1].type == :block and
938
1098
  node.children[1].children[0] == s(:send, nil, :lambda)
939
1099
  then
@@ -949,7 +1109,7 @@ module Ruby2JS
949
1109
  def on_begin(node)
950
1110
  node = super
951
1111
  (node.children.length-2).downto(0) do |i|
952
- if
1112
+ if \
953
1113
  node.children[i].type == :send and
954
1114
  node.children[i].children[0] and
955
1115
  node.children[i].children[0].type == :self and