ruby2js 4.0.0 → 4.0.5

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 (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