ruby2js 4.0.2 → 4.1.1

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.
@@ -26,8 +26,13 @@ module Ruby2JS
26
26
  extend SEXP
27
27
 
28
28
  REACT_IMPORTS = {
29
- React: s(:import, ['React'], s(:attr, nil, :React)),
30
- ReactDOM: s(:import, ['ReactDOM'], s(:attr, nil, :ReactDOM))
29
+ React: s(:import, ['react'], s(:attr, nil, :React)),
30
+ ReactDOM: s(:import, ['react-dom'], s(:attr, nil, :ReactDOM)),
31
+ Preact: s(:import,
32
+ [s(:pair, s(:sym, :as), s(:const, nil, :Preact)),
33
+ s(:pair, s(:sym, :from), s(:str, "preact"))],
34
+ s(:str, '*')),
35
+ PreactHook: s(:import, ["preact/hooks"], [s(:attr, nil, :useState)])
31
36
  }
32
37
 
33
38
  # the following command can be used to generate ReactAttrs:
@@ -71,17 +76,23 @@ module Ruby2JS
71
76
 
72
77
  ReactLifecycle = %w(render componentDidMount shouldComponentUpdate
73
78
  getShapshotBeforeUpdate componentDidUpdate componentWillUnmount
74
- componentDidCatch)
79
+ componentDidCatch componentWillReceiveProps)
75
80
 
76
81
  ReactAttrMap = Hash[ReactAttrs.map {|name| [name.downcase, name]}]
77
82
  ReactAttrMap['for'] = 'htmlFor'
78
- ReactFragment = :'_React.Fragment'
83
+
84
+ PreactAttrMap = {
85
+ htmlFor: 'for',
86
+ onDoubleClick: 'onDblClick',
87
+ tabIndex: 'tabindex'
88
+ }
79
89
 
80
90
  def initialize(*args)
81
91
  @react = nil
82
92
  @reactApply = nil
83
93
  @reactBlock = nil
84
94
  @reactClass = nil
95
+ @reactMethod = nil
85
96
  @react_props = []
86
97
  @react_methods = []
87
98
  @react_filter_functions = false
@@ -118,12 +129,23 @@ module Ruby2JS
118
129
  def on_class(node)
119
130
  cname, inheritance, *body = node.children
120
131
  return super unless cname.children.first == nil
121
- return super unless inheritance == s(:const, nil, :React) or
122
- inheritance == s(:const, nil, :Vue) or
132
+
133
+ if inheritance == s(:const, nil, :React) or
123
134
  inheritance == s(:const, s(:const, nil, :React), :Component) or
124
135
  inheritance == s(:send, s(:const, nil, :React), :Component)
125
136
 
126
- prepend_list << REACT_IMPORTS[:React] if modules_enabled?
137
+ react = :React
138
+ prepend_list << REACT_IMPORTS[:React] if modules_enabled?
139
+
140
+ elsif inheritance == s(:const, nil, :Preact) or
141
+ inheritance == s(:const, s(:const, nil, :Preact), :Component) or
142
+ inheritance == s(:send, s(:const, nil, :Preact), :Component)
143
+
144
+ react = :Preact
145
+ prepend_list << REACT_IMPORTS[:Preact] if modules_enabled?
146
+ else
147
+ return super
148
+ end
127
149
 
128
150
  # traverse down to actual list of class statements
129
151
  if body.length == 1
@@ -141,12 +163,14 @@ module Ruby2JS
141
163
  end
142
164
 
143
165
  begin
144
- react, @react = @react, true
166
+ react, @react = @react, react
145
167
  reactClass, @reactClass = @reactClass, true
146
168
 
147
169
  pairs = []
148
170
 
149
- unless es2015
171
+ createClass = (@react == :React and not es2015)
172
+
173
+ if createClass
150
174
  # automatically capture the displayName for the class
151
175
  pairs << s(:pair, s(:sym, :displayName),
152
176
  s(:str, cname.children.last.to_s))
@@ -156,12 +180,11 @@ module Ruby2JS
156
180
  statics = []
157
181
  body.select {|child| child.type == :defs}.each do |child|
158
182
  _parent, mname, args, *block = child.children
159
- if es2015
160
- block = [s(:autoreturn, *block)] unless child.is_method?
161
- pairs << s(:defs, s(:self), mname, args, *block)
183
+ if not createClass
184
+ pairs << child
162
185
  elsif child.is_method?
163
- statics << s(:pair, s(:sym, mname), process(child.updated(:block,
164
- [s(:send, nil, :proc), args, s(:autoreturn, *block)])))
186
+ statics << s(:pair, s(:sym, mname), child.updated(:block,
187
+ [s(:send, nil, :proc), args, s(:autoreturn, *block)]))
165
188
  elsif \
166
189
  block.length == 1 and
167
190
  Converter::EXPRESSIONS.include? block.first.type
@@ -212,13 +235,53 @@ module Ruby2JS
212
235
  scan_events[node.children]
213
236
  end
214
237
  end
215
- scan_events[body] unless es2015
238
+ scan_events[body] if createClass
216
239
 
217
240
  # append statics (if any)
218
241
  unless statics.empty?
219
242
  pairs << s(:pair, s(:sym, :statics), s(:hash, *statics))
220
243
  end
221
244
 
245
+ # determine if this class can be emitted as a hook
246
+ hook = (es2015 and inheritance.children.first == nil)
247
+ hookinit = nil
248
+ useState = []
249
+ body.each_with_index do |statement, index|
250
+ if statement.type == :def
251
+ method = statement.children.first
252
+ if method == :initialize
253
+ children = statement.children[2..-1]
254
+ children.pop unless children.last
255
+ while children.length == 1 and children.first.type == :begin
256
+ children = children.first.children
257
+ end
258
+ hookinit = index if children.any? {|child| child.type != :ivasgn}
259
+ elsif method == :render
260
+ nil
261
+ elsif ReactLifecycle.include? method.to_s
262
+ hook = false
263
+ elsif not statement.is_method?
264
+ hook = false
265
+ elsif method.to_s.end_with? '='
266
+ hook = false
267
+ end
268
+ elsif statement.type == :defs
269
+ hook = false
270
+ end
271
+ end
272
+
273
+ if hook
274
+ @reactClass = :hook
275
+ @react_props = []
276
+ @react_methods = []
277
+
278
+ if hookinit
279
+ body = body.dup
280
+ hookinit = body.delete_at(hookinit)
281
+ pairs.unshift process hookinit.children[2]
282
+ end
283
+ end
284
+
222
285
  # create a default getInitialState method if there is no such method
223
286
  # and there are either references to instance variables or there are
224
287
  # methods that need to be bound.
@@ -230,7 +293,13 @@ module Ruby2JS
230
293
  then
231
294
  @reactIvars = {pre: [], post: [], asgn: [], ref: [], cond: []}
232
295
  react_walk(node)
233
- if not es2015 and not @reactIvars.values.flatten.empty?
296
+
297
+ if hook
298
+ react_walk(hookinit) if hookinit
299
+ useState = (@reactIvars[:asgn] + @reactIvars[:ref]).uniq
300
+ end
301
+
302
+ if createClass and not @reactIvars.values.flatten.empty?
234
303
  body = [s(:def, :getInitialState, s(:args),
235
304
  s(:return, s(:hash))), *body]
236
305
  elsif not needs_binding.empty? or not @reactIvars.values.flatten.empty?
@@ -242,16 +311,21 @@ module Ruby2JS
242
311
  body.select {|child| child.type == :def}.each do |child|
243
312
  mname, args, *block = child.children
244
313
  @reactMethod = mname
245
- @reactProps = child.updated(:attr, [s(:self), :props])
314
+
315
+ if @reactClass == :hook
316
+ @reactProps = s(:lvar, :"prop$")
317
+ else
318
+ @reactProps = child.updated(:attr, [s(:self), :props])
319
+ end
246
320
 
247
321
  # analyze ivar usage
248
322
  @reactIvars = {pre: [], post: [], asgn: [], ref: [], cond: []}
249
323
  react_walk(child) unless mname == :initialize
250
- @reactIvars[:capture] =
251
- (@reactIvars[:pre] + @reactIvars[:post]).uniq
324
+ @reactIvars[:capture] = (@reactIvars[:pre] + @reactIvars[:post]).uniq
325
+ @reactIvars[:pre] = @reactIvars[:post] = [] if @reactClass == :hook
252
326
 
253
327
  if mname == :initialize
254
- mname = es2015 ? :initialize : :getInitialState
328
+ mname = createClass ? :getInitialState : :initialize
255
329
 
256
330
  # extract real list of statements
257
331
  if block.length == 1
@@ -333,7 +407,7 @@ module Ruby2JS
333
407
  else
334
408
  # wrap multi-line blocks with a React Fragment
335
409
  block = [s(:return,
336
- s(:block, s(:send, nil, ReactFragment), s(:args), *block))]
410
+ s(:block, s(:send, nil, :"_#{@react}.Fragment"), s(:args), *block))]
337
411
  end
338
412
  end
339
413
 
@@ -359,14 +433,14 @@ module Ruby2JS
359
433
  type = :begin if block.first.type == :return
360
434
  end
361
435
 
362
- if es2015
436
+ if createClass
437
+ pairs << s(:pair, s(:sym, mname), child.updated(:block,
438
+ [s(:send, nil, :proc), args, process(s(type, *block))]))
439
+ else
363
440
  pairs << child.updated(
364
441
  ReactLifecycle.include?(mname.to_s) ? :defm : :def,
365
442
  [mname, args, process(s(type, *block))]
366
443
  )
367
- else
368
- pairs << s(:pair, s(:sym, mname), child.updated(:block,
369
- [s(:send, nil, :proc), args, process(s(type, *block))]))
370
444
  end
371
445
 
372
446
  # retain comment
@@ -374,38 +448,70 @@ module Ruby2JS
374
448
  @comments[pairs.last] = @comments[child]
375
449
  end
376
450
  end
451
+
452
+ if createClass
453
+ # emit a createClass statement
454
+ node.updated(:casgn, [nil, cname.children.last,
455
+ s(:send, s(:const, nil, :React), :createClass, s(:hash, *pairs))])
456
+ elsif hook
457
+ initialize = pairs.find_index {|node| node.type == :def and node.children.first == :initialize}
458
+
459
+ hash = {}
460
+ if initialize
461
+ hash = pairs.delete_at(initialize)
462
+ hash = hash.children.last while %i(def begin send).include? hash&.type
463
+ hash = s(:hash) unless hash&.type == :hash
464
+ hash = hash.children.map {|pair|
465
+ [pair.children.first.children.first, pair.children.last]
466
+ }.to_h
467
+ end
468
+
469
+ useState.each do |symbol|
470
+ hash[symbol.to_s[1..-1]] ||= s(:nil)
471
+ end
472
+
473
+ hash.sort.reverse.each do |var, value|
474
+ if @react == :Preact
475
+ hooker = nil
476
+ prepend_list << REACT_IMPORTS[:PreactHook] if modules_enabled?
477
+ else
478
+ hooker = s(:const, nil, :React)
479
+ end
480
+
481
+ setter = 'set' + var[0].upcase + var[1..-1]
482
+ pairs.unshift(s(:masgn, s(:mlhs, s(:lvasgn, var),
483
+ s(:lvasgn, setter)), s(:send, hooker, :useState, value)))
484
+ end
485
+
486
+ render = pairs.find_index {|node| node.type == :defm and node.children.first == :render}
487
+ if render
488
+ render = pairs.delete_at(render)
489
+ pairs.push s(:autoreturn, render.children.last)
490
+ end
491
+
492
+ has_cvar = lambda {|list|
493
+ list.any? {|node|
494
+ next unless Parser::AST::Node === node
495
+ return true if node.type == :cvar
496
+ has_cvar.call(node.children)
497
+ }
498
+ }
499
+ args = has_cvar[node.children] ? s(:args, s(:arg, 'prop$')) : s(:args)
500
+
501
+ node.updated(:def, [cname.children.last, args, s(:begin, *pairs)])
502
+ else
503
+ # emit a class that extends React.Component
504
+ node.updated(:class, [s(:const, nil, cname.children.last),
505
+ s(:attr, s(:const, nil, @react), :Component), *pairs])
506
+ end
377
507
  ensure
378
508
  @react = react
379
509
  @reactClass = reactClass
380
510
  @reactMethod = nil
381
511
  end
382
-
383
- if es2015
384
- # emit a class that extends React.Component
385
- node.updated(:class, [s(:const, nil, cname.children.last),
386
- s(:attr, s(:const, nil, :React), :Component), *pairs])
387
- else
388
- # emit a createClass statement
389
- node.updated(:casgn, [nil, cname.children.last,
390
- s(:send, s(:const, nil, :React), :createClass, s(:hash, *pairs))])
391
- end
392
512
  end
393
513
 
394
514
  def on_send(node)
395
- # convert Vue.utile.defineReactive to class fields or assignments
396
- if node.children.first == s(:send, s(:const, nil, :Vue), :util)
397
- if node.children[1] == :defineReactive
398
- if node.children[2].type == :cvar
399
- return process s(:cvasgn, node.children[2].children.first,
400
- node.children[3])
401
- elsif node.children[2].type == :send
402
- assign = node.children[2]
403
- return assign.updated(nil, [assign.children[0],
404
- assign.children[1].to_s + '=', node.children[3]])
405
- end
406
- end
407
- end
408
-
409
515
  # calls to methods (including getters) defined in this class
410
516
  if node.children[0]==nil and Symbol === node.children[1]
411
517
  if node.is_method?
@@ -423,16 +529,12 @@ module Ruby2JS
423
529
  end
424
530
  end
425
531
 
426
- if node.children.first == s(:const, nil, :Vue)
427
- node = node.updated(nil, [s(:const, nil, :React),
428
- *node.children[1..-1]])
429
- end
430
-
431
532
  if not @react
432
533
  # enable React filtering within React class method calls or
433
534
  # React component calls
434
535
  if \
435
536
  node.children.first == s(:const, nil, :React) or
537
+ node.children.first == s(:const, nil, :Preact) or
436
538
  node.children.first == s(:const, nil, :ReactDOM)
437
539
  then
438
540
  if modules_enabled?
@@ -440,7 +542,8 @@ module Ruby2JS
440
542
  end
441
543
 
442
544
  begin
443
- react, @react = @react, true
545
+ react = @react
546
+ @react = (node.children.first.children.last == :Preact ? :Preact : :React)
444
547
  return on_send(node)
445
548
  ensure
446
549
  @react = react
@@ -461,13 +564,26 @@ module Ruby2JS
461
564
  end
462
565
 
463
566
  elsif \
464
- @reactApply and node.children[1] == :createElement and
465
- node.children[0] == s(:const, nil, :React)
567
+ (@reactApply and node.children[1] == :createElement and
568
+ node.children[0] == s(:const, nil, :React)) or
569
+ (@reactApply and node.children[1] == :h and
570
+ node.children[0] == s(:const, nil, :Preact))
466
571
  then
467
572
  # push results of explicit calls to React.createElement
468
573
  s(:send, s(:gvar, :$_), :push, s(:send, *node.children[0..1],
469
574
  *process_all(node.children[2..-1])))
470
575
 
576
+ elsif \
577
+ @react == :Preact and node.children[1] == :h and node.children[0] == nil
578
+ then
579
+ if @reactApply
580
+ # push results of explicit calls to Preact.h
581
+ s(:send, s(:gvar, :$_), :push, s(:send, s(:const, nil, :Preact), :h,
582
+ *process_all(node.children[2..-1])))
583
+ else
584
+ node.updated(nil, [s(:const, nil, :Preact), :h, *process_all(node.children[2..-1])])
585
+ end
586
+
471
587
  elsif !@jsx and node.children[0] == nil and node.children[1] =~ /^_\w/
472
588
  # map method calls starting with an underscore to React calls
473
589
  # to create an element.
@@ -549,7 +665,12 @@ module Ruby2JS
549
665
  else
550
666
  value = s(:str, values.join(' '))
551
667
  end
552
- pairs.unshift s(:pair, s(:sym, :className), value)
668
+
669
+ if @react == :Preact
670
+ pairs.unshift s(:pair, s(:sym, :class), value)
671
+ else
672
+ pairs.unshift s(:pair, s(:sym, :className), value)
673
+ end
553
674
  end
554
675
 
555
676
  # support controlled form components
@@ -559,13 +680,28 @@ module Ruby2JS
559
680
  ['value', :value].include? pair.children.first.children.first
560
681
  end
561
682
 
562
- # search for the presence of a 'onChange' attribute
683
+ event = (@react == :Preact ? :onInput : :onChange)
684
+
685
+
686
+ # search for the presence of a onInput/onChange attribute
563
687
  onChange = pairs.find_index do |pair|
564
- ['onChange', :onChange].include? pair.children.first.children[0]
688
+ pair.children.first.children[0].to_s == event.to_s
689
+ end
690
+
691
+ if event == :onInput and not onChange
692
+ # search for the presence of a 'onChange' attribute
693
+ onChange = pairs.find_index do |pair|
694
+ pair.children.first.children[0].to_s == 'onChange'
695
+ end
696
+
697
+ if onChange
698
+ pairs[onChange] = s(:pair, s(:sym, event),
699
+ pairs[onChange].children.last)
700
+ end
565
701
  end
566
702
 
567
703
  if value and pairs[value].children.last.type == :ivar and !onChange
568
- pairs << s(:pair, s(:sym, :onChange),
704
+ pairs << s(:pair, s(:sym, event),
569
705
  s(:block, s(:send, nil, :proc), s(:args, s(:arg, :event)),
570
706
  s(:ivasgn, pairs[value].children.last.children.first,
571
707
  s(:attr, s(:attr, s(:lvar, :event), :target), :value))))
@@ -578,7 +714,7 @@ module Ruby2JS
578
714
  end
579
715
 
580
716
  if checked and pairs[checked].children.last.type == :ivar
581
- pairs << s(:pair, s(:sym, :onChange),
717
+ pairs << s(:pair, s(:sym, event),
582
718
  s(:block, s(:send, nil, :proc), s(:args),
583
719
  s(:ivasgn, pairs[checked].children.last.children.first,
584
720
  s(:send, pairs[checked].children.last, :!))))
@@ -586,13 +722,25 @@ module Ruby2JS
586
722
  end
587
723
  end
588
724
 
589
- # replace attribute names with case-sensitive javascript properties
590
- pairs.each_with_index do |pair, index|
591
- next if pair.type == :kwsplat
592
- name = pair.children.first.children.first.downcase
593
- if ReactAttrMap[name] and name.to_s != ReactAttrMap[name]
594
- pairs[index] = pairs[index].updated(nil,
595
- [s(:str, ReactAttrMap[name]), pairs[index].children.last])
725
+ if @react == :Preact
726
+ # replace selected Reactisms with native HTML
727
+ pairs.each_with_index do |pair, index|
728
+ next if pair.type == :kwsplat
729
+ name = pair.children.first.children.first.to_sym
730
+ if PreactAttrMap[name]
731
+ pairs[index] = pairs[index].updated(nil,
732
+ [s(:str, PreactAttrMap[name]), pairs[index].children.last])
733
+ end
734
+ end
735
+ else
736
+ # replace attribute names with case-sensitive javascript properties
737
+ pairs.each_with_index do |pair, index|
738
+ next if pair.type == :kwsplat
739
+ name = pair.children.first.children.first.downcase
740
+ if ReactAttrMap[name] and name.to_s != ReactAttrMap[name]
741
+ pairs[index] = pairs[index].updated(nil,
742
+ [s(:str, ReactAttrMap[name]), pairs[index].children.last])
743
+ end
596
744
  end
597
745
  end
598
746
 
@@ -654,8 +802,14 @@ module Ruby2JS
654
802
  # explicit call to React.createElement
655
803
  next true if arg.children[1] == :createElement and
656
804
  arg.children[0] == s(:const, nil, :React)
657
- next true if arg.children[1] == :createElement and
658
- arg.children[0] == s(:const, nil, :Vue)
805
+
806
+ # explicit call to Preact.h
807
+ next true if arg.children[1] == :h and
808
+ arg.children[0] == s(:const, nil, :Preact)
809
+
810
+ # explicit call to h
811
+ next true if arg.children[1] == :h and
812
+ arg.children[0] == nil
659
813
 
660
814
  # JSX
661
815
  next true if arg.type == :xstr
@@ -717,8 +871,13 @@ module Ruby2JS
717
871
  params.pop if params.last == s(:nil)
718
872
 
719
873
  # construct element using params
720
- element = node.updated(:send, [s(:const, nil, :React),
721
- :createElement, *params])
874
+ if @react == :Preact
875
+ element = node.updated(:send, [s(:const, nil, :Preact),
876
+ :h, *params])
877
+ else
878
+ element = node.updated(:send, [s(:const, nil, :React),
879
+ :createElement, *params])
880
+ end
722
881
 
723
882
  if @reactApply
724
883
  # if apply is set, emit code that pushes result
@@ -846,8 +1005,13 @@ module Ruby2JS
846
1005
  while node != child
847
1006
  if node.children[1] !~ /!$/
848
1007
  # convert method name to hash {className: name} pair
849
- pair = s(:pair, s(:sym, :className),
850
- s(:str, node.children[1].to_s.gsub('_','-')))
1008
+ if @react == :Preact
1009
+ pair = s(:pair, s(:sym, :class),
1010
+ s(:str, node.children[1].to_s.gsub('_','-')))
1011
+ else
1012
+ pair = s(:pair, s(:sym, :className),
1013
+ s(:str, node.children[1].to_s.gsub('_','-')))
1014
+ end
851
1015
  else
852
1016
  # convert method name to hash {id: name} pair
853
1017
  pair = s(:pair, s(:sym, :id),
@@ -917,8 +1081,11 @@ module Ruby2JS
917
1081
  # Base Ruby2JS processing will convert the 'splat' to 'apply'
918
1082
  child = node.children.first
919
1083
  if \
920
- child.children[1] == :createElement and
921
- child.children[0] == s(:const, nil, :React)
1084
+ (child.children[1] == :createElement and
1085
+ child.children[0] == s(:const, nil, :React)) or
1086
+ (child.children[1] == :h and
1087
+ (child.children[0] == s(:const, nil, :Preact) or
1088
+ child.children[0] == nil))
922
1089
  then
923
1090
  begin
924
1091
  reactApply, @reactApply = @reactApply, true
@@ -931,11 +1098,13 @@ module Ruby2JS
931
1098
  @reactApply = reactApply
932
1099
  end
933
1100
 
1101
+ target = child.children[0] || s(:const, nil, :Preact)
1102
+
934
1103
  if reactApply
935
1104
  return child.updated(:send, [s(:gvar, :$_), :push,
936
- s(:send, *child.children[0..1], *params)])
1105
+ s(:send, target, child.children[1], *params)])
937
1106
  else
938
- return child.updated(:send, [*child.children[0..1], *params])
1107
+ return child.updated(:send, [target, child.children[1], *params])
939
1108
  end
940
1109
  end
941
1110
 
@@ -952,7 +1121,7 @@ module Ruby2JS
952
1121
  block = s(:block, s(:send, nil, :proc), s(:args),
953
1122
  *node.children[2..-1])
954
1123
  return on_send node.children.first.updated(:send,
955
- [nil, ReactFragment, block])
1124
+ [nil, :"_#{@react}.Fragment", block])
956
1125
 
957
1126
  elsif !@jsx and child.children[0] == nil and child.children[1] =~ /^_\w/
958
1127
  if node.children[1].children.empty?
@@ -998,13 +1167,17 @@ module Ruby2JS
998
1167
  # convert global variables to refs
999
1168
  def on_gvar(node)
1000
1169
  return super unless @reactClass
1170
+ return super if @reactClass == :hook
1001
1171
  s(:attr, s(:attr, s(:self), :refs), node.children.first.to_s[1..-1])
1002
1172
  end
1003
1173
 
1004
1174
  # convert instance variables to state
1005
1175
  def on_ivar(node)
1006
1176
  return super unless @reactClass
1007
- if @reactMethod and @reactIvars[:capture].include? node.children.first
1177
+
1178
+ if @reactClass == :hook
1179
+ node.updated(:lvar, [node.children.first.to_s[1..-1]])
1180
+ elsif @reactMethod and @reactIvars[:capture].include? node.children.first
1008
1181
  node.updated(:lvar, ["$#{node.children.first[1..-1]}"])
1009
1182
  else
1010
1183
  node.updated(:attr, [s(:attr, s(:self), :state),
@@ -1016,11 +1189,17 @@ module Ruby2JS
1016
1189
  def on_ivasgn(node)
1017
1190
  return super unless @react
1018
1191
 
1192
+ if @reactClass == :hook
1193
+ var = node.children.first.to_s[1..-1]
1194
+ return node.updated(:send, [nil, 'set' + var[0].upcase + var[1..-1],
1195
+ process(node.children.last)])
1196
+ end
1197
+
1019
1198
  if @reactMethod and @reactIvars[:capture].include? node.children.first
1020
1199
  ivar = node.children.first.to_s
1021
1200
  if @reactBlock
1022
1201
  return s(:send, s(:self), :setState, s(:hash, s(:pair,
1023
- s(:lvar, ivar[1..-1]), process(s(:lvasgn, "$#{ivar[1..-1]}",
1202
+ s(:str, ivar[1..-1]), process(s(:lvasgn, "$#{ivar[1..-1]}",
1024
1203
  *node.children[1..-1])))))
1025
1204
  else
1026
1205
  return s(:lvasgn, "$#{ivar[1..-1]}",
@@ -1072,10 +1251,14 @@ module Ruby2JS
1072
1251
  return super unless @react
1073
1252
  return super unless node.children.first.type == :ivasgn
1074
1253
  var = node.children.first.children.first
1075
- if @reactMethod and @reactIvars[:capture].include? var
1254
+ if @reactClass == :hook
1255
+ var = node.children.first.children.first.to_s[1..-1]
1256
+ node.updated(:send, [nil, 'set' + var[0].upcase + var[1..-1],
1257
+ s(:send, s(:lvar, var), *node.children[1..-1])])
1258
+ elsif @reactMethod and @reactIvars[:capture].include? var
1076
1259
  if @reactBlock
1077
1260
  s(:send, s(:self), :setState, s(:hash, s(:pair,
1078
- s(:lvar, var[1..-1]), process(s(node.type,
1261
+ s(:str, var[1..-1]), process(s(node.type,
1079
1262
  s(:lvasgn, "$#{var[1..-1]}"), *node.children[1..-1])))))
1080
1263
  else
1081
1264
  process s(node.type, s(:lvasgn, "$#{var[1..-1]}"),
@@ -1118,9 +1301,13 @@ module Ruby2JS
1118
1301
  return true if node.children[1] == :createElement and
1119
1302
  node.children[0] == s(:const, nil, :React)
1120
1303
 
1121
- # explicit call to Vue.createElement
1122
- return true if node.children[1] == :createElement and
1123
- node.children[0] == s(:const, nil, :Vue)
1304
+ # explicit call to Preact.h
1305
+ return true if node.children[1] == :h and
1306
+ node.children[0] == s(:const, nil, :Preact)
1307
+
1308
+ # explicit call to h
1309
+ return true if node.children[1] == :h and
1310
+ node.children[0] == nil
1124
1311
  end
1125
1312
 
1126
1313
  # wunderbar style call
@@ -1256,6 +1443,7 @@ module Ruby2JS
1256
1443
  @reactIvars = {pre: [], post: [], asgn: [], ref: [], cond: []}
1257
1444
  react_walk(node.children.last)
1258
1445
  @reactIvars[:capture] = (@reactIvars[:pre] + @reactIvars[:post]).uniq
1446
+ @reactIvars[:pre] = @reactIvars[:post] = [] if @reactClass == :hook
1259
1447
  node = super
1260
1448
  block = react_process_ivars([node.children.last.dup])
1261
1449
  node.updated(nil, [*node.children[0..-2], s(:begin, *block)])
@@ -1280,7 +1468,7 @@ module Ruby2JS
1280
1468
  # update ivars that are set and later referenced
1281
1469
  unless @reactIvars[:post].empty?
1282
1470
  updates = @reactIvars[:post].uniq.sort.reverse.map do |ivar|
1283
- s(:pair, s(:lvar, ivar.to_s[1..-1]),
1471
+ s(:pair, s(:str, ivar.to_s[1..-1]),
1284
1472
  s(:lvar, "$#{ivar.to_s[1..-1]}"))
1285
1473
  end
1286
1474
  update = s(:send, s(:self), :setState, s(:hash, *updates))