ruby2js 4.0.1 → 4.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/lib/ruby2js.rb +13 -2
  4. data/lib/ruby2js/converter.rb +31 -0
  5. data/lib/ruby2js/converter/args.rb +1 -1
  6. data/lib/ruby2js/converter/class.rb +6 -4
  7. data/lib/ruby2js/converter/class2.rb +4 -4
  8. data/lib/ruby2js/converter/def.rb +7 -1
  9. data/lib/ruby2js/converter/dstr.rb +3 -1
  10. data/lib/ruby2js/converter/for.rb +1 -1
  11. data/lib/ruby2js/converter/next.rb +10 -2
  12. data/lib/ruby2js/converter/redo.rb +14 -0
  13. data/lib/ruby2js/converter/return.rb +1 -1
  14. data/lib/ruby2js/converter/send.rb +1 -0
  15. data/lib/ruby2js/converter/taglit.rb +9 -2
  16. data/lib/ruby2js/converter/while.rb +1 -1
  17. data/lib/ruby2js/converter/whilepost.rb +1 -1
  18. data/lib/ruby2js/filter/functions.rb +44 -9
  19. data/lib/ruby2js/filter/lit-element.rb +202 -0
  20. data/lib/ruby2js/filter/react.rb +155 -91
  21. data/lib/ruby2js/filter/stimulus.rb +4 -2
  22. data/lib/ruby2js/filter/tagged_templates.rb +4 -2
  23. data/lib/ruby2js/rails.rb +6 -59
  24. data/lib/ruby2js/serializer.rb +16 -11
  25. data/lib/ruby2js/sprockets.rb +40 -0
  26. data/lib/ruby2js/version.rb +2 -2
  27. data/lib/tasks/README.md +4 -0
  28. data/lib/tasks/install/app/javascript/elements/index.js +2 -0
  29. data/lib/tasks/install/config/webpack/loaders/ruby2js.js +20 -0
  30. data/lib/tasks/install/litelement.rb +9 -0
  31. data/lib/tasks/install/preact.rb +3 -0
  32. data/lib/tasks/install/react.rb +2 -0
  33. data/lib/tasks/install/stimulus-sprockets.rb +32 -0
  34. data/lib/tasks/install/stimulus-webpacker.rb +5 -0
  35. data/lib/tasks/install/webpacker.rb +80 -0
  36. data/lib/tasks/nodetest.js +47 -0
  37. data/lib/tasks/ruby2js_tasks.rake +47 -0
  38. data/ruby2js.gemspec +1 -1
  39. metadata +18 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b77ba98b59e2dd3f500c4fa6c4974d6a082757becd31eb47efa841b51513ec28
4
- data.tar.gz: 3a868aa5a10b6562ad156c47bec44aed4060338f6aa747e90298dd890ae3095f
3
+ metadata.gz: 46b6cba0b33ebf62cb966def9b680eda35f5e84ff0c3adaea058fc2aae242269
4
+ data.tar.gz: 6c5843992eccd24e8460b16f805eab16701d70d85e8ef4dcaffee948243666c0
5
5
  SHA512:
6
- metadata.gz: 2837170756fe4b6ca1f369d1dfe10c2ee6613f0572d54104354fb10df0b2f57dbe38c1dc4a44fa53f093af26bc6b826897144fbd5c5f0f6029885898878ff581
7
- data.tar.gz: 97b02c3f49d12c7eb6f84e5a504e6b5210f04bf4450f8923ee38927714aa749968839b67ac2ad04f508f7a4cc34010eab06ad4b3cc408415390107bc1a04d623
6
+ metadata.gz: 6910f39474f2c22c321b91ad742ab10f7fff08bc5408e53f9b4d0cdee4f4fead414519a1b31bf7e6f781dc31da71fbda55bc747132c0d78e8eeb62a8693ca419
7
+ data.tar.gz: 07b1ddbff32682c2ea3fa65570e40f97907eea4765d00ea4b7921bca5156f83f8f855178ab13ed3fc70b9edb8035fc801cdb4031c6f8176e044699099ac7dccd
data/README.md CHANGED
@@ -3,7 +3,6 @@ Ruby2JS
3
3
 
4
4
  Minimal yet extensible Ruby to JavaScript conversion.
5
5
 
6
- [![Build Status](https://travis-ci.org/rubys/ruby2js.svg)](https://travis-ci.org/rubys/ruby2js)
7
6
  [![Gem Version](https://badge.fury.io/rb/ruby2js.svg)](https://badge.fury.io/rb/ruby2js)
8
7
  [![Gitter](https://badges.gitter.im/ruby2js/community.svg)](https://gitter.im/ruby2js/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
9
8
 
data/lib/ruby2js.rb CHANGED
@@ -92,6 +92,17 @@ module Ruby2JS
92
92
  include_only(options[:include_only]) if options[:include_only]
93
93
  include(options[:include]) if options[:include]
94
94
  exclude(options[:exclude]) if options[:exclude]
95
+
96
+ filters = options[:filters] || DEFAULTS
97
+ @modules_enabled =
98
+ (defined? Ruby2JS::Filter::ESM and
99
+ filters.include? Ruby2JS::Filter::ESM) or
100
+ (defined? Ruby2JS::Filter::CJS and
101
+ filters.include? Ruby2JS::Filter::CJS)
102
+ end
103
+
104
+ def modules_enabled?
105
+ @modules_enabled
95
106
  end
96
107
 
97
108
  def es2015
@@ -169,7 +180,7 @@ module Ruby2JS
169
180
  def on_xnode(node); end
170
181
  def on_export(node); end
171
182
  def on_import(node); end
172
- def on_taglit(node); end
183
+ def on_taglit(node); on_pair(node); end
173
184
 
174
185
  # provide a method so filters can call 'super'
175
186
  def on_sym(node); node; end
@@ -279,7 +290,7 @@ module Ruby2JS
279
290
 
280
291
  def self.parse(source, file=nil, line=1)
281
292
  buffer = Parser::Source::Buffer.new(file, line)
282
- buffer.source = source.encode('utf-8')
293
+ buffer.source = source.encode('UTF-8')
283
294
  parser = Parser::CurrentRuby.new
284
295
  parser.diagnostics.all_errors_are_fatal = true
285
296
  parser.diagnostics.consumer = lambda {|diagnostic| nil}
@@ -60,12 +60,14 @@ module Ruby2JS
60
60
  @class_parent = nil
61
61
  @class_name = nil
62
62
  @jsx = false
63
+ @autobind = true
63
64
 
64
65
  @eslevel = :es5
65
66
  @strict = false
66
67
  @comparison = :equality
67
68
  @or = :logical
68
69
  @underscored_private = true
70
+ @redoable = false
69
71
  end
70
72
 
71
73
  def width=(width)
@@ -243,6 +245,34 @@ module Ruby2JS
243
245
  end
244
246
  end
245
247
 
248
+ def redoable(block)
249
+ save_redoable = @redoable
250
+
251
+ has_redo = proc do |node|
252
+ node.children.any? do |child|
253
+ next false unless child.is_a? Parser::AST::Node
254
+ next true if child.type == :redo
255
+ next false if %i[for while while_post until until_post].include? child.type
256
+ has_redo[child]
257
+ end
258
+ end
259
+
260
+ @redoable = has_redo[@ast]
261
+
262
+ if @redoable
263
+ put es2015 ? 'let ' : 'var '
264
+ put "redo$#@sep"
265
+ puts 'do {'
266
+ put "redo$ = false#@sep"
267
+ scope block
268
+ put "#@nl} while(redo$)"
269
+ else
270
+ scope block
271
+ end
272
+ ensure
273
+ @redoable = save_redoable
274
+ end
275
+
246
276
  def timestamp(file)
247
277
  super
248
278
 
@@ -335,6 +365,7 @@ require 'ruby2js/converter/nil'
335
365
  require 'ruby2js/converter/nthref'
336
366
  require 'ruby2js/converter/opasgn'
337
367
  require 'ruby2js/converter/prototype'
368
+ require 'ruby2js/converter/redo'
338
369
  require 'ruby2js/converter/regexp'
339
370
  require 'ruby2js/converter/return'
340
371
  require 'ruby2js/converter/self'
@@ -32,7 +32,7 @@ module Ruby2JS
32
32
  elsif kw.type == :kwoptarg
33
33
  put kw.children.first
34
34
  unless kw.children.last == s(:send, nil, :undefined)
35
- put ' = '; parse kw.children.last
35
+ put '='; parse kw.children.last
36
36
  end
37
37
  elsif kw.type == :kwrestarg
38
38
  raise 'Rest arg requires ES2018' unless es2018
@@ -36,7 +36,8 @@ module Ruby2JS
36
36
  end
37
37
 
38
38
  if inheritance
39
- init = s(:def, :initialize, s(:args), s(:super))
39
+ parent = @namespace.find(inheritance)&.[](:constructor)
40
+ init = s(:def, :initialize, parent || s(:args), s(:zsuper))
40
41
  else
41
42
  init = s(:def, :initialize, s(:args), nil)
42
43
  end
@@ -58,7 +59,7 @@ module Ruby2JS
58
59
  m = m.updated(:def, m.children[1..-1])
59
60
  end
60
61
 
61
- node = if m.type == :def
62
+ node = if m.type == :def or m.type == :defm
62
63
  if m.children.first == :initialize and !visible[:initialize]
63
64
  # constructor: remove from body and overwrite init function
64
65
  init = m
@@ -313,7 +314,7 @@ module Ruby2JS
313
314
  # prepend constructor
314
315
  if init
315
316
  constructor = init.updated(:constructor, [name, *init.children[1..-1]])
316
- @comments[constructor] = @comments[init] unless @comments[init].empty?
317
+ visible[:constructor] = init.children[1]
317
318
 
318
319
  if @ast.type == :class_extend or extend
319
320
  if es2015
@@ -329,6 +330,7 @@ module Ruby2JS
329
330
  end
330
331
  end
331
332
 
333
+ @comments[constructor] = @comments[init] unless @comments[init].empty?
332
334
  body.unshift constructor
333
335
  end
334
336
 
@@ -349,7 +351,7 @@ module Ruby2JS
349
351
  self.ivars = ivars
350
352
  @class_name = class_name
351
353
  @class_parent = class_parent
352
- @rbstack.pop
354
+ @namespace.defineProps @rbstack.pop
353
355
  @namespace.leave unless @ast.type == :class_module
354
356
  end
355
357
  end
@@ -99,7 +99,6 @@ module Ruby2JS
99
99
  end
100
100
  walk[@ast]
101
101
 
102
- # process leading initializers in constructor
103
102
  while constructor.length == 1 and constructor.first.type == :begin
104
103
  constructor = constructor.first.children.dup
105
104
  end
@@ -116,6 +115,7 @@ module Ruby2JS
116
115
  put 'static #$' + cvar.to_s[2..-1]
117
116
  end
118
117
 
118
+ # process leading initializers in constructor
119
119
  while constructor.length > 0 and constructor.first.type == :ivasgn
120
120
  put(index == 0 ? @nl : @sep)
121
121
  index += 1
@@ -156,7 +156,7 @@ module Ruby2JS
156
156
  end
157
157
  end
158
158
 
159
- if m.type == :def || m.type == :defm || m.type == :async
159
+ if %i[def defm deff async].include? m.type
160
160
  @prop = m.children.first
161
161
 
162
162
  if @prop == :initialize and !@rbstack.last[:initialize]
@@ -190,11 +190,11 @@ module Ruby2JS
190
190
  end
191
191
 
192
192
  elsif \
193
- [:defs, :asyncs].include? m.type and m.children.first.type == :self
193
+ [:defs, :defp, :asyncs].include? m.type and m.children.first.type == :self
194
194
  then
195
195
 
196
196
  @prop = "static #{m.children[1]}"
197
- if not m.is_method?
197
+ if m.type == :defp or not m.is_method?
198
198
  @prop = "static get #{m.children[1]}"
199
199
  m = m.updated(m.type, [*m.children[0..2],
200
200
  s(:autoreturn, m.children[3])])
@@ -142,7 +142,13 @@ module Ruby2JS
142
142
  end
143
143
 
144
144
  if style == :expression
145
- expr.type == :hash ? group(expr) : wrap('(', ')') { parse(expr) }
145
+ if expr.type == :taglit
146
+ parse expr
147
+ elsif expr.type == :hash
148
+ group(expr)
149
+ else
150
+ wrap('(', ')') { parse(expr) }
151
+ end
146
152
  elsif body.type == :begin and body.children.length == 0
147
153
  put "{}"
148
154
  else
@@ -25,7 +25,9 @@ module Ruby2JS
25
25
  put '`'
26
26
  children.each do |child|
27
27
  if child.type == :str
28
- str = child.children.first.inspect[1..-2].gsub('${', '$\{').gsub('`', '\\\`')
28
+ str = child.children.first.inspect[1..-2].
29
+ gsub('${', '$\{').gsub('`', '\\\`')
30
+ str = str.gsub(/\\"/, '"') unless str.include? '\\\\'
29
31
  if heredoc
30
32
  put! str.gsub("\\n", "\n")
31
33
  else
@@ -28,7 +28,7 @@ module Ruby2JS
28
28
  else
29
29
  put (@ast.type==:for_of ? ' of ' : ' in '); parse expression;
30
30
  end
31
- puts ') {'; scope block; sput '}'
31
+ puts ') {'; redoable block; sput '}'
32
32
  ensure
33
33
  @next_token = next_token
34
34
  @vars = vars if es2015
@@ -5,8 +5,16 @@ module Ruby2JS
5
5
  # (int 1))
6
6
 
7
7
  handle :next do |n=nil|
8
- raise Error.new("next argument #{ n.inspect }", @ast) if n
9
- put @next_token.to_s
8
+ if @next_token == :return
9
+ put 'return'
10
+ if n
11
+ put ' '
12
+ parse n
13
+ end
14
+ else
15
+ raise Error.new("next argument #{ n.inspect }", @ast) if n
16
+ put @next_token.to_s
17
+ end
10
18
  end
11
19
  end
12
20
  end
@@ -0,0 +1,14 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (redo)
5
+
6
+ handle :redo do
7
+ unless @redoable and @next_token == :continue
8
+ raise Error.new("redo outside of loop", @ast)
9
+ end
10
+
11
+ put "redo$ = true#{@sep}continue"
12
+ end
13
+ end
14
+ end
@@ -15,7 +15,7 @@ module Ruby2JS
15
15
  EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send, :attr,
16
16
  :str, :sym, :dstr, :dsym, :cvar, :ivar, :zsuper, :super, :or, :and,
17
17
  :block, :const, :true, :false, :xnode, :taglit, :self,
18
- :op_asgn, :and_asgn, :or_asgn ]
18
+ :op_asgn, :and_asgn, :or_asgn, :taglit, :gvar ]
19
19
 
20
20
  handle :autoreturn do |*statements|
21
21
  return if statements == [nil]
@@ -153,6 +153,7 @@ module Ruby2JS
153
153
  if receiver
154
154
  if receiver.type == :autobind
155
155
  autobind = receiver = receiver.children.first
156
+ autobind = nil unless @autobind
156
157
  end
157
158
 
158
159
  group_receiver = receiver.type == :send &&
@@ -6,8 +6,15 @@ module Ruby2JS
6
6
  # (dstr)
7
7
 
8
8
  handle :taglit do |tag, *children|
9
- put tag.children.first
10
- parse_all(*children, join: '')
9
+ begin
10
+ # disable autobinding in tag literals
11
+ save_autobind, @autobind = @autobind, false
12
+
13
+ put tag.children.first
14
+ parse_all(*children, join: '')
15
+ ensure
16
+ @autobind = save_autobind
17
+ end
11
18
  end
12
19
  end
13
20
  end
@@ -22,7 +22,7 @@ module Ruby2JS
22
22
  end
23
23
  end
24
24
 
25
- put 'while ('; parse condition; puts ') {'; scope block; sput '}'
25
+ put 'while ('; parse condition; puts ') {'; redoable block; sput '}'
26
26
  ensure
27
27
  @next_token = next_token
28
28
  end
@@ -11,7 +11,7 @@ module Ruby2JS
11
11
  begin
12
12
  next_token, @next_token = @next_token, :continue
13
13
 
14
- puts 'do {'; scope block; sput '} while ('; parse condition; put ')'
14
+ puts 'do {'; redoable block; sput '} while ('; parse condition; put ')'
15
15
  ensure
16
16
  @next_token = next_token
17
17
  end
@@ -210,10 +210,10 @@ module Ruby2JS
210
210
  s(:block, s(:send, nil, :proc), s(:args, s(:arg, :s)),
211
211
  s(:send, s(:lvar, :s), :slice, s(:int, 1))))
212
212
  else
213
- # str.match(/.../g).map(s => s.match(/.../).slice(1))
213
+ # (str.match(/.../g) || []).map(s => s.match(/.../).slice(1))
214
214
  s(:block, s(:send,
215
- s(:send, process(target), :match, gpattern), :map),
216
- s(:args, s(:arg, :s)),
215
+ s(:or, s(:send, process(target), :match, gpattern), s(:array)),
216
+ :map), s(:args, s(:arg, :s)),
217
217
  s(:return, s(:send, s(:send, s(:lvar, :s), :match, arg),
218
218
  :slice, s(:int, 1))))
219
219
  end
@@ -227,14 +227,19 @@ module Ruby2JS
227
227
  if before.type == :regexp
228
228
  before = before.updated(:regexp, [*before.children[0...-1],
229
229
  s(:regopt, :g, *before.children.last)])
230
- elsif before.type == :str
230
+ elsif before.type == :str and not es2021
231
231
  before = before.updated(:regexp,
232
232
  [s(:str, Regexp.escape(before.children.first)), s(:regopt, :g)])
233
233
  end
234
234
  if after.type == :str
235
235
  after = s(:str, after.children.first.gsub(/\\(\d)/, "$\\1"))
236
236
  end
237
- process node.updated nil, [target, :replace, before, after]
237
+
238
+ if es2021
239
+ process node.updated nil, [target, :replaceAll, before, after]
240
+ else
241
+ process node.updated nil, [target, :replace, before, after]
242
+ end
238
243
 
239
244
  elsif method == :ord and args.length == 0
240
245
  if target.type == :str
@@ -508,6 +513,36 @@ module Ruby2JS
508
513
  elsif method == :floor and args.length == 0
509
514
  process S(:send, s(:const, nil, :Math), :floor, target)
510
515
 
516
+ elsif method == :rand and target == nil
517
+ if args.length == 0
518
+ process S(:send!, s(:const, nil, :Math), :random)
519
+ elsif %i[irange erange].include? args.first.type
520
+ range = args.first
521
+ multiplier = s(:send, range.children.last, :-, range.children.first)
522
+ if range.children.all? {|child| child.type == :int}
523
+ multiplier = s(:int, range.children.last.children.last - range.children.first.children.last)
524
+ multiplier = s(:int, multiplier.children.first + 1) if range.type == :irange
525
+ elsif range.type == :irange
526
+ if multiplier.children.last.type == :int
527
+ diff = multiplier.children.last.children.last - 1
528
+ multiplier = s(:send, *multiplier.children[0..1], s(:int, diff))
529
+ multiplier = multiplier.children.first if diff == 0
530
+ multiplier = s(:send, multiplier.children[0], :+, s(:int, -diff)) if diff < 0
531
+ else
532
+ multiplier = s(:send, multiplier, :+, s(:int, 1))
533
+ end
534
+ end
535
+ raw = s(:send, s(:send, s(:const, nil, :Math), :random), :*, multiplier)
536
+ if range.children.first != s(:int, 0)
537
+ raw = s(:send, raw, :+, range.children.first)
538
+ end
539
+ process S(:send, nil, :parseInt, raw)
540
+ else
541
+ process S(:send, nil, :parseInt,
542
+ s(:send, s(:send, s(:const, nil, :Math), :random),
543
+ :*, args.first))
544
+ end
545
+
511
546
  elsif method == :sum and args.length == 0
512
547
  process S(:send, target, :reduce, s(:block, s(:send, nil, :proc),
513
548
  s(:args, s(:arg, :a), s(:arg, :b)),
@@ -737,10 +772,10 @@ module Ruby2JS
737
772
  body.any? {|statement| statement.type == :def and
738
773
  statement.children.first == :initialize}
739
774
  then
740
- body.unshift S(:def, :initialize, s(:args, s(:arg, :message)),
741
- s(:begin, S(:send, s(:self), :message=, s(:lvar, :message)),
742
- S(:send, s(:self), :name=, s(:sym, name.children[1])),
743
- S(:send, s(:self), :stack=, s(:send, s(:send, nil, :Error,
775
+ body.unshift s(:def, :initialize, s(:args, s(:arg, :message)),
776
+ s(:begin, s(:send, s(:self), :message=, s(:lvar, :message)),
777
+ s(:send, s(:self), :name=, s(:sym, name.children[1])),
778
+ s(:send, s(:self), :stack=, s(:attr, s(:send, nil, :Error,
744
779
  s(:lvar, :message)), :stack))))
745
780
  end
746
781
 
@@ -0,0 +1,202 @@
1
+ require 'ruby2js'
2
+
3
+ module Ruby2JS
4
+ module Filter
5
+ module LitElement
6
+ include SEXP
7
+ extend SEXP
8
+
9
+ LITELEMENT_IMPORT = s(:import,
10
+ [s(:pair, s(:sym, :from), s(:str, "lit-element"))],
11
+ [s(:const, nil, :LitElement), s(:attr, nil, :css), s(:attr, nil, :html)])
12
+
13
+ def initialize(node)
14
+ super
15
+ @le_props = nil
16
+ end
17
+
18
+ def on_ivar(node)
19
+ return super unless @le_props&.include?(node.children.first)
20
+ s(:attr, s(:self), node.children.first.to_s[1..-1])
21
+ end
22
+
23
+ def on_ivasgn(node)
24
+ return super unless @le_props&.include?(node.children.first)
25
+ s(:send, s(:self), node.children.first.to_s[1..-1]+'=',
26
+ process(node.children[1]))
27
+ end
28
+
29
+ def on_class(node)
30
+ cname, inheritance, *body = node.children
31
+ return super unless inheritance == s(:const, nil, :LitElement)
32
+
33
+ @le_props = {}
34
+ le_walk(node)
35
+
36
+ prepend_list << LITELEMENT_IMPORT if modules_enabled?
37
+
38
+ nodes = body.dup
39
+ if nodes.length == 1 and nodes.first&.type == :begin
40
+ nodes = nodes.first.children.dup
41
+ end
42
+
43
+ # insert/update static get properties() {}
44
+ unless @le_props.empty?
45
+ values = nodes.find_index {|child|
46
+ child.type == :defs and child.children[0..1] == [s(:self), :properties]
47
+ }
48
+
49
+ if values == nil
50
+ nodes.unshift s(:defp, s(:self), :properties, s(:args), s(:return,
51
+ s(:hash, *@le_props.map {|name, type| s(:pair, s(:str, name.to_s[1..-1]),
52
+ s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String))))})))
53
+ elsif nodes[values].children[3].type == :hash
54
+ le_props = @le_props.map {|name, type|
55
+ [s(:sym, name.to_s[1..-1].to_sym),
56
+ s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String)))]
57
+ }.to_h.merge(
58
+ nodes[values].children[3].children.map {|pair| pair.children}.to_h
59
+ )
60
+
61
+ nodes[values] = nodes[values].updated(nil,
62
+ [*nodes[values].children[0..2], s(:hash,
63
+ *le_props.map{|name, value| s(:pair, name, value)})])
64
+ end
65
+ end
66
+
67
+ # render of a string is converted to a taglit :html
68
+ render = nodes.find_index {|child|
69
+ child.type == :def and child.children.first == :render
70
+ }
71
+ if render and %i[str dstr].include?(nodes[render].children[2].type)
72
+ nodes[render] = nodes[render].updated(:deff,
73
+ [*nodes[render].children[0..1],
74
+ s(:autoreturn, html_wrap(nodes[render].children[2]))])
75
+ end
76
+
77
+ # self.styles returning string is converted to a taglit :css
78
+ styles = nodes.find_index {|child|
79
+ child.type == :defs and child.children[0..1] == [s(:self), :styles]
80
+ }
81
+ if styles and %i[str dstr].include?(nodes[styles].children[3].type)
82
+ string = nodes[styles].children[3]
83
+ string = s(:dstr, string) if string.type == :str
84
+ children = string.children.dup
85
+
86
+ while children.length > 1 and children.last.type == :str and
87
+ children.last.children.last.strip == ''
88
+ children.pop
89
+ end
90
+
91
+ if children.last.type == :str
92
+ children << s(:str, children.pop.children.first.chomp)
93
+ end
94
+
95
+ nodes[styles] = nodes[styles].updated(nil,
96
+ [*nodes[styles].children[0..2],
97
+ s(:autoreturn, s(:taglit, s(:sym, :css),
98
+ s(:dstr, *children)))])
99
+ end
100
+
101
+ # insert super calls into initializer
102
+ initialize = nodes.find_index {|child|
103
+ child.type == :def and child.children.first == :initialize
104
+ }
105
+ if initialize and nodes[initialize].children.length == 3
106
+ statements = nodes[initialize].children[2].children
107
+
108
+ if statements.length == 1 and statements.children.first.type == :begin
109
+ statements = statements.children
110
+ end
111
+
112
+ unless statements.any? {|statement| %i[super zuper].include? statement.type}
113
+ nodes[initialize] = nodes[initialize].updated(nil,
114
+ [*nodes[initialize].children[0..1],
115
+ s(:begin, s(:zsuper), *statements)])
116
+ end
117
+ end
118
+
119
+ props = @le_props.keys.map {|prop| [prop.to_sym, s(:self)]}.to_h
120
+
121
+ nodes.unshift s(:defineProps, props)
122
+
123
+ nodes.pop unless nodes.last
124
+
125
+ node.updated(nil, [*node.children[0..1], s(:begin, *process_all(nodes))])
126
+ ensure
127
+ @le_props = nil
128
+ end
129
+
130
+ def html_wrap(node)
131
+ if node.type == :str and node.children.first.strip.start_with? '<'
132
+ s(:taglit, s(:sym, :html), s(:dstr, node))
133
+ elsif node.type == :dstr
134
+ prefix = ''
135
+ node.children.each do |child|
136
+ break unless child.type == :str
137
+ prefix += child.children.first
138
+ end
139
+
140
+ return node unless prefix.strip.start_with? '<'
141
+
142
+ children = node.children.map do |child|
143
+ if child.type == :str
144
+ child
145
+ else
146
+ html_wrap(child)
147
+ end
148
+ end
149
+
150
+ while children.length > 1 and children.last.type == :str and
151
+ children.last.children.last.strip == ''
152
+ children.pop
153
+ end
154
+
155
+ if children.last.type == :str
156
+ children << s(:str, children.pop.children.first.chomp)
157
+ end
158
+
159
+ s(:taglit, s(:sym, :html), node.updated(nil, children))
160
+ elsif node.type == :begin
161
+ node.updated(nil, node.children.map {|child| html_wrap(child)})
162
+ elsif node.type == :if
163
+ node.updated(nil, [node.children.first,
164
+ *node.children[1..2].map {|child| html_wrap(child)}])
165
+ elsif node.type == :block and
166
+ node.children.first.children[1] == :map
167
+ node.updated(nil, [*node.children[0..1],
168
+ html_wrap(node.children[2])])
169
+ else
170
+ node
171
+ end
172
+ end
173
+
174
+ # analyze ivar usage
175
+ def le_walk(node)
176
+ node.children.each do |child|
177
+ next unless Parser::AST::Node === child
178
+ le_walk(child)
179
+
180
+ if child.type == :ivar
181
+ @le_props[child.children.first] ||= nil
182
+ elsif child.type == :ivasgn
183
+ @le_props[child.children.first] = case child.children.last.type
184
+ when :str, :dstr
185
+ :String
186
+ when :array
187
+ :Array
188
+ when :int, :float
189
+ :Number
190
+ when :true, :false
191
+ :Boolean
192
+ else
193
+ @le_props[child.children.first] || :Object
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ DEFAULTS.push LitElement
201
+ end
202
+ end