ruby2js 4.0.1 → 4.1.0

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