ruby2js 4.0.0 → 4.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/lib/ruby2js.rb +16 -3
  4. data/lib/ruby2js/converter.rb +31 -0
  5. data/lib/ruby2js/converter/args.rb +6 -1
  6. data/lib/ruby2js/converter/class.rb +3 -0
  7. data/lib/ruby2js/converter/class2.rb +8 -5
  8. data/lib/ruby2js/converter/def.rb +8 -2
  9. data/lib/ruby2js/converter/dstr.rb +3 -1
  10. data/lib/ruby2js/converter/for.rb +1 -1
  11. data/lib/ruby2js/converter/hash.rb +19 -1
  12. data/lib/ruby2js/converter/logical.rb +1 -1
  13. data/lib/ruby2js/converter/next.rb +10 -2
  14. data/lib/ruby2js/converter/redo.rb +14 -0
  15. data/lib/ruby2js/converter/return.rb +2 -1
  16. data/lib/ruby2js/converter/send.rb +30 -4
  17. data/lib/ruby2js/converter/taglit.rb +9 -2
  18. data/lib/ruby2js/converter/while.rb +1 -1
  19. data/lib/ruby2js/converter/whilepost.rb +1 -1
  20. data/lib/ruby2js/converter/xstr.rb +1 -2
  21. data/lib/ruby2js/filter/camelCase.rb +2 -0
  22. data/lib/ruby2js/filter/functions.rb +39 -11
  23. data/lib/ruby2js/filter/lit-element.rb +202 -0
  24. data/lib/ruby2js/filter/react.rb +6 -16
  25. data/lib/ruby2js/filter/require.rb +2 -6
  26. data/lib/ruby2js/filter/stimulus.rb +4 -2
  27. data/lib/ruby2js/filter/tagged_templates.rb +4 -2
  28. data/lib/ruby2js/jsx.rb +19 -1
  29. data/lib/ruby2js/rails.rb +6 -59
  30. data/lib/ruby2js/serializer.rb +16 -11
  31. data/lib/ruby2js/sprockets.rb +40 -0
  32. data/lib/ruby2js/version.rb +1 -1
  33. data/lib/tasks/README.md +4 -0
  34. data/lib/tasks/install/app/javascript/elements/index.js +2 -0
  35. data/lib/tasks/install/litelement.rb +9 -0
  36. data/lib/tasks/install/react.rb +2 -0
  37. data/lib/tasks/install/stimulus-sprockets.rb +32 -0
  38. data/lib/tasks/install/stimulus-webpacker.rb +5 -0
  39. data/lib/tasks/install/webpacker.rb +97 -0
  40. data/lib/tasks/nodetest.js +47 -0
  41. data/lib/tasks/ruby2js_tasks.rake +42 -0
  42. data/ruby2js.gemspec +1 -1
  43. metadata +18 -6
@@ -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
@@ -5,9 +5,8 @@ module Ruby2JS
5
5
  # (str 'a'))
6
6
 
7
7
  handle :xstr do |*children|
8
- str = eval capture { parse_all(*children) }
9
-
10
8
  if @binding
9
+ str = eval capture { parse_all(*children) }
11
10
  puts @binding.eval(str).to_s
12
11
  else
13
12
  raise SecurityError.new('Insecure operation, eval without binding option')
@@ -11,6 +11,8 @@ module Ruby2JS
11
11
 
12
12
  ALLOWLIST = %w{
13
13
  attr_accessor
14
+ attr_reader
15
+ attr_writer
14
16
  method_missing
15
17
  }
16
18
 
@@ -1,4 +1,5 @@
1
1
  require 'ruby2js'
2
+
2
3
  require 'regexp_parser/scanner'
3
4
 
4
5
  module Ruby2JS
@@ -7,7 +8,7 @@ module Ruby2JS
7
8
  include SEXP
8
9
 
9
10
  # require explicit opt-in to to class => constructor mapping
10
- Filter.exclude :class
11
+ Filter.exclude :class, :call
11
12
 
12
13
  VAR_TO_ASSIGN = {
13
14
  lvar: :lvasgn,
@@ -23,7 +24,7 @@ module Ruby2JS
23
24
 
24
25
  def on_send(node)
25
26
  target, method, *args = node.children
26
- return super if excluded?(method)
27
+ return super if excluded?(method) and method != :call
27
28
 
28
29
  if [:max, :min].include? method and args.length == 0
29
30
  if target.type == :array
@@ -36,13 +37,10 @@ module Ruby2JS
36
37
  return super
37
38
  end
38
39
 
39
- elsif method == :call and target and target.type == :ivar
40
- process S(:send, s(:self), "_#{target.children.first.to_s[1..-1]}",
41
- *args)
40
+ elsif method == :call and target and
41
+ (%i[ivar cvar].include?(target.type) or not excluded?(:call))
42
42
 
43
- elsif method == :call and target and target.type == :cvar
44
- process S(:send, s(:attr, s(:self), :constructor),
45
- "_#{target.children.first.to_s[2..-1]}", *args)
43
+ S(:call, process(target), nil, *process_all(args))
46
44
 
47
45
  elsif method == :keys and args.length == 0 and node.is_method?
48
46
  process S(:send, s(:const, nil, :Object), :keys, target)
@@ -212,10 +210,10 @@ module Ruby2JS
212
210
  s(:block, s(:send, nil, :proc), s(:args, s(:arg, :s)),
213
211
  s(:send, s(:lvar, :s), :slice, s(:int, 1))))
214
212
  else
215
- # str.match(/.../g).map(s => s.match(/.../).slice(1))
213
+ # (str.match(/.../g) || []).map(s => s.match(/.../).slice(1))
216
214
  s(:block, s(:send,
217
- s(:send, process(target), :match, gpattern), :map),
218
- s(:args, s(:arg, :s)),
215
+ s(:or, s(:send, process(target), :match, gpattern), s(:array)),
216
+ :map), s(:args, s(:arg, :s)),
219
217
  s(:return, s(:send, s(:send, s(:lvar, :s), :match, arg),
220
218
  :slice, s(:int, 1))))
221
219
  end
@@ -510,6 +508,36 @@ module Ruby2JS
510
508
  elsif method == :floor and args.length == 0
511
509
  process S(:send, s(:const, nil, :Math), :floor, target)
512
510
 
511
+ elsif method == :rand and target == nil
512
+ if args.length == 0
513
+ process S(:send!, s(:const, nil, :Math), :random)
514
+ elsif %i[irange erange].include? args.first.type
515
+ range = args.first
516
+ multiplier = s(:send, range.children.last, :-, range.children.first)
517
+ if range.children.all? {|child| child.type == :int}
518
+ multiplier = s(:int, range.children.last.children.last - range.children.first.children.last)
519
+ multiplier = s(:int, multiplier.children.first + 1) if range.type == :irange
520
+ elsif range.type == :irange
521
+ if multiplier.children.last.type == :int
522
+ diff = multiplier.children.last.children.last - 1
523
+ multiplier = s(:send, *multiplier.children[0..1], s(:int, diff))
524
+ multiplier = multiplier.children.first if diff == 0
525
+ multiplier = s(:send, multiplier.children[0], :+, s(:int, -diff)) if diff < 0
526
+ else
527
+ multiplier = s(:send, multiplier, :+, s(:int, 1))
528
+ end
529
+ end
530
+ raw = s(:send, s(:send, s(:const, nil, :Math), :random), :*, multiplier)
531
+ if range.children.first != s(:int, 0)
532
+ raw = s(:send, raw, :+, range.children.first)
533
+ end
534
+ process S(:send, nil, :parseInt, raw)
535
+ else
536
+ process S(:send, nil, :parseInt,
537
+ s(:send, s(:send, s(:const, nil, :Math), :random),
538
+ :*, args.first))
539
+ end
540
+
513
541
  elsif method == :sum and args.length == 0
514
542
  process S(:send, target, :reduce, s(:block, s(:send, nil, :proc),
515
543
  s(:args, s(:arg, :a), s(:arg, :b)),
@@ -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
@@ -26,8 +26,8 @@ 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
31
  }
32
32
 
33
33
  # the following command can be used to generate ReactAttrs:
@@ -85,7 +85,6 @@ module Ruby2JS
85
85
  @react_props = []
86
86
  @react_methods = []
87
87
  @react_filter_functions = false
88
- @react_imports = false
89
88
  @jsx = false
90
89
  super
91
90
  end
@@ -102,15 +101,6 @@ module Ruby2JS
102
101
  @react_filter_functions = true
103
102
  end
104
103
 
105
- if \
106
- (defined? Ruby2JS::Filter::ESM and
107
- filters.include? Ruby2JS::Filter::ESM) or
108
- (defined? Ruby2JS::Filter::CJS and
109
- filters.include? Ruby2JS::Filter::CJS)
110
- then
111
- @react_imports = true
112
- end
113
-
114
104
  if \
115
105
  defined? Ruby2JS::Filter::JSX and
116
106
  filters.include? Ruby2JS::Filter::JSX
@@ -133,7 +123,7 @@ module Ruby2JS
133
123
  inheritance == s(:const, s(:const, nil, :React), :Component) or
134
124
  inheritance == s(:send, s(:const, nil, :React), :Component)
135
125
 
136
- prepend_list << REACT_IMPORTS[:React] if @react_imports
126
+ prepend_list << REACT_IMPORTS[:React] if modules_enabled?
137
127
 
138
128
  # traverse down to actual list of class statements
139
129
  if body.length == 1
@@ -222,7 +212,7 @@ module Ruby2JS
222
212
  scan_events[node.children]
223
213
  end
224
214
  end
225
- scan_events[body]
215
+ scan_events[body] unless es2015
226
216
 
227
217
  # append statics (if any)
228
218
  unless statics.empty?
@@ -323,7 +313,7 @@ module Ruby2JS
323
313
  elsif mname == :render and not react_wunderbar_free(block, true)
324
314
  if \
325
315
  block.length != 1 or not block.last or
326
- not [:send, :block].include? block.last.type
316
+ not %i[send block xstr].include? block.last.type
327
317
  then
328
318
  if @jsx
329
319
  while block.length == 1 and block.first.type == :begin
@@ -445,7 +435,7 @@ module Ruby2JS
445
435
  node.children.first == s(:const, nil, :React) or
446
436
  node.children.first == s(:const, nil, :ReactDOM)
447
437
  then
448
- if @react_imports
438
+ if modules_enabled?
449
439
  prepend_list << REACT_IMPORTS[node.children.first.children.last]
450
440
  end
451
441
 
@@ -111,6 +111,7 @@ module Ruby2JS
111
111
 
112
112
  importname = Pathname.new(filename).relative_path_from(Pathname.new(dirname)).to_s
113
113
  importname = Pathname.new(@require_relative).join(importname).to_s
114
+ importname = "./#{importname}" unless importname.start_with? '.'
114
115
 
115
116
  prepend_list << s(:import, importname, *imports)
116
117
 
@@ -152,12 +153,7 @@ module Ruby2JS
152
153
  end
153
154
  end
154
155
  else
155
- begin
156
- require_expr, @require_expr = @require_expr, true
157
- super
158
- ensure
159
- @require_expr = require_expr
160
- end
156
+ super
161
157
  end
162
158
  end
163
159
 
@@ -51,8 +51,10 @@ module Ruby2JS
51
51
  @stim_classes = Set.new
52
52
  stim_walk(node)
53
53
 
54
- prepend_list << (@options[:import_from_skypack] ?
55
- STIMULUS_IMPORT_SKYPACK : STIMULUS_IMPORT)
54
+ if modules_enabled?
55
+ prepend_list << (@options[:import_from_skypack] ?
56
+ STIMULUS_IMPORT_SKYPACK : STIMULUS_IMPORT)
57
+ end
56
58
 
57
59
  nodes = body
58
60
  if nodes.length == 1 and nodes.first&.type == :begin