ruby2js 4.1.4 → 4.2.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.
- checksums.yaml +4 -4
- data/bin/ruby2js +4 -0
- data/demo/ruby2js.rb +677 -0
- data/lib/ruby2js/converter/def.rb +3 -1
- data/lib/ruby2js/converter/logical.rb +3 -3
- data/lib/ruby2js/converter/return.rb +1 -1
- data/lib/ruby2js/converter.rb +2 -2
- data/lib/ruby2js/demo.rb +28 -0
- data/lib/ruby2js/filter/active_functions.rb +9 -1
- data/lib/ruby2js/filter/camelCase.rb +2 -2
- data/lib/ruby2js/filter/esm.rb +28 -14
- data/lib/ruby2js/filter/functions.rb +11 -3
- data/lib/ruby2js/filter/lit-element.rb +2 -218
- data/lib/ruby2js/filter/lit.rb +290 -0
- data/lib/ruby2js/filter/react.rb +18 -5
- data/lib/ruby2js/filter/stimulus.rb +2 -2
- data/lib/ruby2js/filter/underscore.rb +23 -17
- data/lib/ruby2js/version.rb +2 -2
- data/lib/tasks/install/{litelement.rb → lit-webpacker.rb} +2 -2
- data/lib/tasks/install/stimulus-rollup.rb +44 -0
- data/lib/tasks/install/stimulus-sprockets.rb +16 -23
- data/lib/tasks/install/stimulus-webpacker.rb +3 -0
- data/lib/tasks/ruby2js_tasks.rake +19 -5
- data/ruby2js.gemspec +3 -1
- metadata +9 -4
@@ -0,0 +1,290 @@
|
|
1
|
+
require 'ruby2js'
|
2
|
+
|
3
|
+
module Ruby2JS
|
4
|
+
module Filter
|
5
|
+
module Lit
|
6
|
+
include SEXP
|
7
|
+
extend SEXP
|
8
|
+
|
9
|
+
LITELEMENT_IMPORT = s(:import,
|
10
|
+
[s(:pair, s(:sym, :from), s(:str, "lit"))],
|
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
|
+
process 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
|
+
return super unless node.children.length > 1
|
26
|
+
|
27
|
+
process s(:send, s(:self), node.children.first.to_s[1..-1]+'=',
|
28
|
+
process(node.children[1]))
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_op_asgn(node)
|
32
|
+
return super unless node.children.first.type == :ivasgn
|
33
|
+
var = node.children.first.children.first
|
34
|
+
return super unless @le_props&.include?(var)
|
35
|
+
super node.updated(nil, [s(:attr, s(:attr, nil, :this),
|
36
|
+
var.to_s[1..-1]), *node.children[1..-1]])
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_class(node)
|
40
|
+
class_name, inheritance, *body = node.children
|
41
|
+
return super unless inheritance == s(:const, nil, :LitElement)
|
42
|
+
|
43
|
+
@le_props = {}
|
44
|
+
le_walk(node)
|
45
|
+
|
46
|
+
prepend_list << LITELEMENT_IMPORT if modules_enabled?
|
47
|
+
|
48
|
+
nodes = body.dup
|
49
|
+
if nodes.length == 1 and nodes.first&.type == :begin
|
50
|
+
nodes = nodes.first.children.dup
|
51
|
+
end
|
52
|
+
|
53
|
+
# insert/update static get properties() {}
|
54
|
+
unless @le_props.empty?
|
55
|
+
values = nodes.find_index {|child|
|
56
|
+
(child.type == :defs and child.children[0..1] == [s(:self), :properties]) or
|
57
|
+
(child.type == :send and child.children[0..1] == [s(:self), :properties=])
|
58
|
+
}
|
59
|
+
|
60
|
+
if values == nil
|
61
|
+
if es2022
|
62
|
+
nodes.unshift process(s(:casgn, nil, :properties,
|
63
|
+
s(:hash, *@le_props.map {|name, type| s(:pair, s(:sym, name.to_s[1..-1]),
|
64
|
+
s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String))))})))
|
65
|
+
else
|
66
|
+
nodes.unshift process(s(:defp, s(:self), :properties, s(:args), s(:return,
|
67
|
+
s(:hash, *@le_props.map {|name, type| s(:pair, s(:sym, name.to_s[1..-1]),
|
68
|
+
s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String))))}))))
|
69
|
+
end
|
70
|
+
elsif nodes[values].children.last.type == :hash
|
71
|
+
le_props = @le_props.map {|name, type|
|
72
|
+
[s(:sym, name.to_s[1..-1].to_sym),
|
73
|
+
s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String)))]
|
74
|
+
}.to_h.merge(
|
75
|
+
nodes[values].children.last.children.map {|pair| pair.children}.to_h
|
76
|
+
)
|
77
|
+
|
78
|
+
nodes[values] = nodes[values].updated(nil,
|
79
|
+
[*nodes[values].children[0..-2], s(:hash,
|
80
|
+
*le_props.map{|name, value| s(:pair, name, value)})])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# customElement is converted to customElements.define
|
85
|
+
customElement = nodes.find_index {|child|
|
86
|
+
child&.type == :send and (child.children[0..1] == [nil, :customElement] || child.children[0..1] == [nil, :custom_element])
|
87
|
+
}
|
88
|
+
if customElement and nodes[customElement].children.length == 3
|
89
|
+
nodes[customElement] = nodes[customElement].updated(nil,
|
90
|
+
[s(:attr, nil, :customElements), :define,
|
91
|
+
nodes[customElement].children.last, class_name])
|
92
|
+
end
|
93
|
+
|
94
|
+
# render of a string is converted to a taglit :html
|
95
|
+
render = nodes.find_index {|child|
|
96
|
+
child&.type == :def and child.children.first == :render
|
97
|
+
}
|
98
|
+
if render and %i[str dstr begin if block].include?(nodes[render].children[2]&.type)
|
99
|
+
nodes[render] = nodes[render].updated(:deff,
|
100
|
+
[*nodes[render].children[0..1],
|
101
|
+
s(:autoreturn, html_wrap(nodes[render].children[2]))])
|
102
|
+
end
|
103
|
+
|
104
|
+
# self.styles returning string is converted to a taglit :css
|
105
|
+
styles = nodes.find_index {|child|
|
106
|
+
(child&.type == :ivasgn and child.children[0] == :@styles) or
|
107
|
+
(child&.type == :defs and child.children[0..1] == [s(:self), :styles]) or
|
108
|
+
(child&.type == :send and child.children[0..1] == [s(:self), :styles=])
|
109
|
+
}
|
110
|
+
if styles and %i[str dstr].include?(nodes[styles].children.last&.type)
|
111
|
+
string = nodes[styles].children.last
|
112
|
+
string = s(:dstr, string) if string.type == :str
|
113
|
+
children = string.children.dup
|
114
|
+
|
115
|
+
while children.length > 1 and children.last.type == :str and
|
116
|
+
children.last.children.last.strip == ''
|
117
|
+
children.pop
|
118
|
+
end
|
119
|
+
|
120
|
+
if children.last.type == :str
|
121
|
+
children << s(:str, children.pop.children.first.chomp)
|
122
|
+
end
|
123
|
+
|
124
|
+
if es2022
|
125
|
+
nodes[styles] = nodes[styles].updated(:casgn,
|
126
|
+
[nil, :styles, s(:taglit, s(:sym, :css),
|
127
|
+
s(:dstr, *children))])
|
128
|
+
else
|
129
|
+
nodes[styles] = nodes[styles].updated(:defp,
|
130
|
+
[s(:self), :styles, s(:args),
|
131
|
+
s(:autoreturn, s(:taglit, s(:sym, :css),
|
132
|
+
s(:dstr, *children)))])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# insert super calls into initializer
|
137
|
+
initialize = nodes.find_index {|child|
|
138
|
+
child&.type == :def and child.children.first == :initialize
|
139
|
+
}
|
140
|
+
if initialize and nodes[initialize].children.length == 3
|
141
|
+
statements = nodes[initialize].children[2..-1]
|
142
|
+
|
143
|
+
if statements.length == 1 and statements.first.type == :begin
|
144
|
+
statements = statements.first.children
|
145
|
+
end
|
146
|
+
|
147
|
+
unless statements.any? {|statement| %i[super zuper].include? statement.type}
|
148
|
+
nodes[initialize] = nodes[initialize].updated(nil,
|
149
|
+
[*nodes[initialize].children[0..1],
|
150
|
+
s(:begin, s(:zsuper), *statements)])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# props/methods inherited from LitElement
|
155
|
+
props = {
|
156
|
+
hasUpdated: s(:self),
|
157
|
+
performUpdate: s(:autobind, s(:self)),
|
158
|
+
renderRoot: s(:self),
|
159
|
+
requestUpdate: s(:autobind, s(:self)),
|
160
|
+
shadowRoot: s(:self),
|
161
|
+
updateComplete: s(:self),
|
162
|
+
}
|
163
|
+
|
164
|
+
# local props
|
165
|
+
props.merge! @le_props.keys.map {|prop| [prop.to_sym, s(:self)]}.to_h
|
166
|
+
|
167
|
+
nodes.unshift s(:defineProps, props)
|
168
|
+
|
169
|
+
nodes.pop unless nodes.last
|
170
|
+
|
171
|
+
node.updated(nil, [*node.children[0..1], s(:begin, *process_all(nodes))])
|
172
|
+
ensure
|
173
|
+
@le_props = nil
|
174
|
+
end
|
175
|
+
|
176
|
+
def html_wrap(node)
|
177
|
+
return node unless node.is_a?(Parser::AST::Node)
|
178
|
+
|
179
|
+
if node.type == :str and node.children.first.strip.start_with? '<'
|
180
|
+
s(:taglit, s(:sym, :html), s(:dstr, node))
|
181
|
+
elsif node.type == :dstr
|
182
|
+
prefix = ''
|
183
|
+
node.children.each do |child|
|
184
|
+
break unless child.type == :str
|
185
|
+
prefix += child.children.first
|
186
|
+
end
|
187
|
+
|
188
|
+
return node unless prefix.strip.start_with? '<'
|
189
|
+
|
190
|
+
children = node.children.map do |child|
|
191
|
+
if child.type == :str
|
192
|
+
child
|
193
|
+
else
|
194
|
+
html_wrap(child)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
while children.length > 1 and children.last.type == :str and
|
199
|
+
children.last.children.last.strip == ''
|
200
|
+
children.pop
|
201
|
+
end
|
202
|
+
|
203
|
+
if children.last.type == :str
|
204
|
+
children << s(:str, children.pop.children.first.chomp)
|
205
|
+
end
|
206
|
+
|
207
|
+
s(:taglit, s(:sym, :html), node.updated(nil, children))
|
208
|
+
elsif node.type == :begin
|
209
|
+
node.updated(nil, node.children.map {|child| html_wrap(child)})
|
210
|
+
elsif node.type == :if
|
211
|
+
node.updated(nil, [node.children.first,
|
212
|
+
*node.children[1..2].map {|child| html_wrap(child)}])
|
213
|
+
elsif node.type == :block and
|
214
|
+
node.children.first.children[1] == :map
|
215
|
+
node.updated(nil, [*node.children[0..1],
|
216
|
+
html_wrap(node.children[2])])
|
217
|
+
else
|
218
|
+
node
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def on_def(node)
|
223
|
+
node = super
|
224
|
+
return node if [:constructor, :initialize].include?(node.children.first)
|
225
|
+
|
226
|
+
children = node.children[1..-1]
|
227
|
+
|
228
|
+
node.updated nil, [node.children[0], children.first,
|
229
|
+
*(children[1..-1].map {|child| html_wrap(child) })]
|
230
|
+
end
|
231
|
+
|
232
|
+
# analyze ivar usage
|
233
|
+
def le_walk(node)
|
234
|
+
node.children.each do |child|
|
235
|
+
next unless child.is_a? Parser::AST::Node
|
236
|
+
|
237
|
+
if child.type == :ivar
|
238
|
+
next if child.children.first.to_s.start_with?("@_")
|
239
|
+
|
240
|
+
@le_props[child.children.first] ||= nil
|
241
|
+
elsif child.type == :ivasgn || child.type == :op_asgn
|
242
|
+
prop = child.children.first
|
243
|
+
unless prop.is_a? Symbol
|
244
|
+
prop = prop.children.first if prop.type == :ivasgn
|
245
|
+
next unless prop.is_a? Symbol
|
246
|
+
end
|
247
|
+
|
248
|
+
next if prop.to_s.start_with?("@_")
|
249
|
+
|
250
|
+
@le_props[prop] = case child.children.last.type
|
251
|
+
when :str, :dstr
|
252
|
+
:String
|
253
|
+
when :array
|
254
|
+
:Array
|
255
|
+
when :int, :float
|
256
|
+
:Number
|
257
|
+
when :true, :false
|
258
|
+
:Boolean
|
259
|
+
else
|
260
|
+
@le_props[prop] || :Object
|
261
|
+
end
|
262
|
+
else
|
263
|
+
le_walk(child)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def on_send(node)
|
269
|
+
target, method, *args = node.children
|
270
|
+
|
271
|
+
return super if target
|
272
|
+
return super unless %i{query queryAll queryAsync}.include? method
|
273
|
+
return super unless args.length == 1
|
274
|
+
|
275
|
+
result = s(:csend, s(:attr, s(:self), :renderRoot),
|
276
|
+
(method == :query ? 'querySelector' : 'querySelectorAll'),
|
277
|
+
args.first)
|
278
|
+
|
279
|
+
if method == :queryAsync
|
280
|
+
result = s(:block, s(:send, s(:attr, s(:self), :updateComplete),
|
281
|
+
:then), s(:args), result)
|
282
|
+
end
|
283
|
+
|
284
|
+
result
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
DEFAULTS.push Lit
|
289
|
+
end
|
290
|
+
end
|
data/lib/ruby2js/filter/react.rb
CHANGED
@@ -41,9 +41,16 @@ module Ruby2JS
|
|
41
41
|
#
|
42
42
|
def self.genAttrs
|
43
43
|
unless RUBY_ENGINE == 'opal'
|
44
|
-
require '
|
45
|
-
|
46
|
-
|
44
|
+
require 'nokogiri'
|
45
|
+
require 'uri'
|
46
|
+
require 'net/http'
|
47
|
+
|
48
|
+
page = 'https://reactjs.org/docs/dom-elements.html'
|
49
|
+
uri = URI.parse(page)
|
50
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
51
|
+
http.use_ssl = true
|
52
|
+
data = http.get(uri.request_uri).body
|
53
|
+
doc = Nokogiri::HTML5::Document.parse(data)
|
47
54
|
|
48
55
|
# delete contents of page prior to the list of supported attributes
|
49
56
|
attrs = doc.at('a[name=supported-attributes]')
|
@@ -51,7 +58,7 @@ module Ruby2JS
|
|
51
58
|
attrs.previous_sibling.remove while attrs and attrs.previous_sibling
|
52
59
|
|
53
60
|
# extract attribute names with uppercase chars from code and format
|
54
|
-
attrs = doc.search('code').map(&:text).join(' ')
|
61
|
+
attrs = doc.search('div[data-language=text] pre code').map(&:text).join(' ')
|
55
62
|
attrs = attrs.split(/\s+/).grep(/[A-Z]/).sort.uniq.join(' ')
|
56
63
|
puts "ReactAttrs = %w(#{attrs})".gsub(/(.{1,72})(\s+|\Z)/, "\\1\n")
|
57
64
|
end
|
@@ -1492,7 +1499,13 @@ module Ruby2JS
|
|
1492
1499
|
source = Ruby2JS.jsx2_rb(source)
|
1493
1500
|
ast = Ruby2JS.parse(source).first
|
1494
1501
|
ast = s(:block, s(:send, nil, :_), s(:args), ast) if ast.type == :begin
|
1495
|
-
|
1502
|
+
|
1503
|
+
begin
|
1504
|
+
react, @react = @react, @react || :react
|
1505
|
+
process ast
|
1506
|
+
ensure
|
1507
|
+
@react = react
|
1508
|
+
end
|
1496
1509
|
end
|
1497
1510
|
end
|
1498
1511
|
|
@@ -9,12 +9,12 @@ module Ruby2JS
|
|
9
9
|
|
10
10
|
STIMULUS_IMPORT = s(:import,
|
11
11
|
[s(:pair, s(:sym, :as), s(:const, nil, :Stimulus)),
|
12
|
-
s(:pair, s(:sym, :from), s(:str, "stimulus"))],
|
12
|
+
s(:pair, s(:sym, :from), s(:str, "@hotwired/stimulus"))],
|
13
13
|
s(:str, '*'))
|
14
14
|
|
15
15
|
STIMULUS_IMPORT_SKYPACK = s(:import,
|
16
16
|
[s(:pair, s(:sym, :as), s(:const, nil, :Stimulus)),
|
17
|
-
s(:pair, s(:sym, :from), s(:str, "https://cdn.skypack.dev/stimulus"))],
|
17
|
+
s(:pair, s(:sym, :from), s(:str, "https://cdn.skypack.dev/@hotwired/stimulus"))],
|
18
18
|
s(:str, '*'))
|
19
19
|
|
20
20
|
def initialize(*args)
|
@@ -18,6 +18,12 @@ module Ruby2JS
|
|
18
18
|
else
|
19
19
|
super
|
20
20
|
end
|
21
|
+
elsif [:take, :drop].include? method and node.is_method?
|
22
|
+
process S(:send, s(:lvar, :_), method, node.children[0], node.children[2])
|
23
|
+
elsif method == :each_slice and node.is_method?
|
24
|
+
process S(:send, s(:lvar, :_), :chunk, node.children[0], node.children[2])
|
25
|
+
elsif [:min, :max].include? method and node.children.length == 2
|
26
|
+
process S(:send, s(:lvar, :_), method, node.children[0])
|
21
27
|
elsif method == :sample and node.children.length <= 3
|
22
28
|
process S(:send, s(:lvar, :_), :sample, node.children[0],
|
23
29
|
*node.children[2..-1])
|
@@ -62,14 +68,14 @@ module Ruby2JS
|
|
62
68
|
elsif method == :reduce
|
63
69
|
if node.children.length == 3 and node.children[2].type == :sym
|
64
70
|
# input: a.reduce(:+)
|
65
|
-
# output: _.reduce(_.rest(a),
|
71
|
+
# output: _.reduce(_.rest(a),
|
66
72
|
# proc {|memo, item| return memo+item},
|
67
73
|
# a[0])
|
68
|
-
process S(:send, s(:lvar, :_), :reduce,
|
74
|
+
process S(:send, s(:lvar, :_), :reduce,
|
69
75
|
s(:send, s(:lvar, :_), :rest, node.children.first),
|
70
|
-
s(:block, s(:send, nil, :proc),
|
76
|
+
s(:block, s(:send, nil, :proc),
|
71
77
|
s(:args, s(:arg, :memo), s(:arg, :item)),
|
72
|
-
s(:autoreturn, s(:send, s(:lvar, :memo),
|
78
|
+
s(:autoreturn, s(:send, s(:lvar, :memo),
|
73
79
|
node.children[2].children.first, s(:lvar, :item)))),
|
74
80
|
s(:send, node.children.first, :[], s(:int, 0)))
|
75
81
|
elsif node.children.last.type == :block_pass
|
@@ -79,9 +85,9 @@ module Ruby2JS
|
|
79
85
|
# input: a.reduce(n, :+)
|
80
86
|
# output: _.reduce(a, proc {|memo, item| return memo+item}, n)
|
81
87
|
process S(:send, s(:lvar, :_), :reduce, node.children.first,
|
82
|
-
s(:block, s(:send, nil, :proc),
|
88
|
+
s(:block, s(:send, nil, :proc),
|
83
89
|
s(:args, s(:arg, :memo), s(:arg, :item)),
|
84
|
-
s(:autoreturn, s(:send, s(:lvar, :memo),
|
90
|
+
s(:autoreturn, s(:send, s(:lvar, :memo),
|
85
91
|
node.children[3].children.first, s(:lvar, :item)))),
|
86
92
|
node.children[2])
|
87
93
|
else
|
@@ -93,8 +99,8 @@ module Ruby2JS
|
|
93
99
|
# input: a.compact!
|
94
100
|
# output: a.splice(0, a.length, *a.compact)
|
95
101
|
target = node.children.first
|
96
|
-
process S(:send, target, :splice, s(:int, 0),
|
97
|
-
s(:attr, target, :length), s(:splat, s(:send, target,
|
102
|
+
process S(:send, target, :splice, s(:int, 0),
|
103
|
+
s(:attr, target, :length), s(:splat, s(:send, target,
|
98
104
|
:"#{method.to_s[0..-2]}", *node.children[2..-1])))
|
99
105
|
else
|
100
106
|
super
|
@@ -111,14 +117,14 @@ module Ruby2JS
|
|
111
117
|
# output: _.sortBy {return expression}
|
112
118
|
method = method.to_s.sub(/\_by$/,'By').to_sym
|
113
119
|
process S(:block, s(:send, s(:lvar, :_), method,
|
114
|
-
call.children.first), node.children[1],
|
120
|
+
call.children.first), node.children[1],
|
115
121
|
s(:autoreturn, node.children[2]))
|
116
122
|
elsif [:find, :reject].include? method
|
117
123
|
if call.children.length == 2
|
118
124
|
# input: a.find {|item| item > 0}
|
119
125
|
# output: _.find(a) {|item| return item > 0}
|
120
|
-
process S(:block, s(:send, s(:lvar, :_), method,
|
121
|
-
call.children.first), node.children[1],
|
126
|
+
process S(:block, s(:send, s(:lvar, :_), method,
|
127
|
+
call.children.first), node.children[1],
|
122
128
|
s(:autoreturn, node.children[2]))
|
123
129
|
else
|
124
130
|
super
|
@@ -127,25 +133,25 @@ module Ruby2JS
|
|
127
133
|
elsif method == :times and call.children.length == 2
|
128
134
|
# input: 5.times {|i| console.log i}
|
129
135
|
# output: _.find(5) {|i| console.log(i)}
|
130
|
-
process S(:block, s(:send, s(:lvar, :_), method,
|
136
|
+
process S(:block, s(:send, s(:lvar, :_), method,
|
131
137
|
call.children.first), node.children[1], node.children[2])
|
132
138
|
|
133
139
|
elsif method == :reduce
|
134
140
|
if call.children.length == 2
|
135
141
|
# input: a.reduce {|memo, item| memo+item}
|
136
|
-
# output: _.reduce(_.rest(a),
|
142
|
+
# output: _.reduce(_.rest(a),
|
137
143
|
# proc {|memo, item| return memo+item},
|
138
144
|
# a[0])
|
139
|
-
process S(:call, s(:lvar, :_), :reduce,
|
145
|
+
process S(:call, s(:lvar, :_), :reduce,
|
140
146
|
s(:send, s(:lvar, :_), :rest, call.children.first),
|
141
|
-
s(:block, s(:send, nil, :proc),
|
147
|
+
s(:block, s(:send, nil, :proc),
|
142
148
|
node.children[1], s(:autoreturn, node.children[2])),
|
143
149
|
s(:send, call.children.first, :[], s(:int, 0)))
|
144
150
|
elsif call.children.length == 3
|
145
151
|
# input: a.reduce(n) {|memo, item| memo+item}
|
146
152
|
# output: _.reduce(a, proc {|memo, item| return memo+item}, n)
|
147
153
|
process S(:call, s(:lvar, :_), :reduce, call.children.first,
|
148
|
-
s(:block, s(:send, nil, :proc),
|
154
|
+
s(:block, s(:send, nil, :proc),
|
149
155
|
node.children[1], s(:autoreturn, node.children[2])),
|
150
156
|
call.children[2])
|
151
157
|
end
|
@@ -155,7 +161,7 @@ module Ruby2JS
|
|
155
161
|
# output: a.splice(0, a.length, *a.map {expression})
|
156
162
|
method = :"#{method.to_s[0..-2]}"
|
157
163
|
target = call.children.first
|
158
|
-
process S(:call, target, :splice, s(:splat, s(:send, s(:array,
|
164
|
+
process S(:call, target, :splice, s(:splat, s(:send, s(:array,
|
159
165
|
s(:int, 0), s(:attr, target, :length)), :concat,
|
160
166
|
s(:block, s(:send, target, method, *call.children[2..-1]),
|
161
167
|
*node.children[1..-1]))))
|
data/lib/ruby2js/version.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
# install rollup plugin
|
2
|
+
run "yarn add @ruby2js/rollup-plugin"
|
3
|
+
|
4
|
+
# configure rollup for ruby2js with stimulus filters
|
5
|
+
insert_into_file Rails.root.join("rollup.config.js").to_s,
|
6
|
+
"import ruby2js from '@ruby2js/rollup-plugin';\n",
|
7
|
+
after: /import resolve from .*\n/
|
8
|
+
|
9
|
+
insert_into_file Rails.root.join("rollup.config.js").to_s,
|
10
|
+
<<-CONFIG, after: "resolve()\n"
|
11
|
+
,ruby2js({
|
12
|
+
eslevel: 2020,
|
13
|
+
autoexports: 'default',
|
14
|
+
filters: ['stimulus', 'esm', 'functions']
|
15
|
+
})
|
16
|
+
CONFIG
|
17
|
+
|
18
|
+
# monkey patch stimulus:manifest:update to find .rb.js controllers too.
|
19
|
+
# See https://github.com/hotwired/stimulus-rails/issues/76
|
20
|
+
append_to_file Rails.root.join('config/application.rb').to_s,
|
21
|
+
"\n" + <<~'CONFIG'
|
22
|
+
require 'stimulus/manifest'
|
23
|
+
|
24
|
+
module Stimulus::Manifest
|
25
|
+
def import_and_register_controller(controllers_path, controller_path)
|
26
|
+
controller_path = controller_path.relative_path_from(controllers_path).to_s
|
27
|
+
module_path = controller_path.split('.').first
|
28
|
+
controller_class_name = module_path.camelize.gsub(/::/, "__")
|
29
|
+
tag_name = module_path.remove(/_controller/).gsub(/_/, "-").gsub(/\//, "--")
|
30
|
+
|
31
|
+
<<~JS
|
32
|
+
|
33
|
+
import #{controller_class_name} from "./#{controller_path}"
|
34
|
+
application.register("#{tag_name}", #{controller_class_name})
|
35
|
+
JS
|
36
|
+
end
|
37
|
+
|
38
|
+
def extract_controllers_from(directory)
|
39
|
+
(directory.children.select { |e| e.to_s =~ /_controller\.js(\.\w+)?$/ } +
|
40
|
+
directory.children.select(&:directory?).collect { |d| extract_controllers_from(d) }
|
41
|
+
).flatten.sort
|
42
|
+
end
|
43
|
+
end
|
44
|
+
CONFIG
|
@@ -1,32 +1,25 @@
|
|
1
1
|
create_file Rails.root.join('config/initializers/ruby2js.rb').to_s,
|
2
2
|
<<~CONFIG
|
3
|
-
require '
|
4
|
-
require 'ruby2js/filter/functions'
|
5
|
-
require 'ruby2js/filter/stimulus'
|
3
|
+
require 'stimulus/manifest'
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
module Stimulus::Manifest
|
6
|
+
def import_and_register_controller(controllers_path, controller_path)
|
7
|
+
controller_path = controller_path.relative_path_from(controllers_path).to_s
|
8
|
+
module_path = controller_path.split('.').first
|
9
|
+
controller_class_name = module_path.camelize.gsub(/::/, "__")
|
10
|
+
tag_name = module_path.remove(/_controller/).gsub(/_/, "-").gsub(/\//, "--")
|
11
11
|
|
12
|
-
|
12
|
+
<<~JS
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
next unless Sprockets.transformers[value]["application/javascript"]
|
19
|
-
[key, '.js']
|
20
|
-
}.compact.to_h)
|
21
|
-
|
22
|
-
Dir[path.join('**/*')].map {|file|
|
23
|
-
file_ext, web_ext = Sprockets::PathUtils.match_path_extname(file, exts)
|
24
|
-
next unless file_ext
|
25
|
-
|
26
|
-
next unless File.file? file
|
14
|
+
import #{controller_class_name} from "./#{controller_path}"
|
15
|
+
application.register("#{tag_name}", #{controller_class_name})
|
16
|
+
JS
|
17
|
+
end
|
27
18
|
|
28
|
-
|
29
|
-
|
19
|
+
def extract_controllers_from(directory)
|
20
|
+
(directory.children.select { |e| e.to_s =~ /_controller\.js(\.\w+)?$/ } +
|
21
|
+
directory.children.select(&:directory?).collect { |d| extract_controllers_from(d) }
|
22
|
+
).flatten.sort
|
30
23
|
end
|
31
24
|
end
|
32
25
|
CONFIG
|
@@ -3,3 +3,6 @@ eval IO.read "#{__dir__}/webpacker.rb"
|
|
3
3
|
|
4
4
|
insert_into_file Rails.root.join("app/javascript/controllers/index.js").to_s,
|
5
5
|
'(\\.rb)?', after: '_controller\\.js'
|
6
|
+
|
7
|
+
insert_into_file Rails.root.join("app/javascript/packs/application.js").to_s,
|
8
|
+
"import \"../controllers\"\n", after: "import \"channels\"\n"
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
if defined? Thor
|
2
|
+
Thor::Actions::WARNINGS[:unchanged_no_flag] = 'unchanged'
|
3
|
+
end
|
2
4
|
|
3
5
|
def template(location)
|
4
6
|
system "#{RbConfig.ruby} #{Rails.root.join("bin")}/rails app:template " +
|
@@ -9,7 +11,7 @@ namespace :ruby2js do
|
|
9
11
|
namespace :install do
|
10
12
|
desc "Install Ruby2JS with LitElement support"
|
11
13
|
task :litelement do
|
12
|
-
template 'install/
|
14
|
+
template 'install/lit-webpacker.rb'
|
13
15
|
end
|
14
16
|
|
15
17
|
desc "Install Ruby2JS with Preact support"
|
@@ -24,9 +26,9 @@ namespace :ruby2js do
|
|
24
26
|
end
|
25
27
|
|
26
28
|
namespace :stimulus do
|
27
|
-
desc "Install Ruby2JS with Stimulus
|
28
|
-
task :
|
29
|
-
template 'install/stimulus-
|
29
|
+
desc "Install Ruby2JS with Stimulus Rollup support"
|
30
|
+
task :rollup do
|
31
|
+
template 'install/stimulus-rollup.rb'
|
30
32
|
end
|
31
33
|
|
32
34
|
desc "Install Ruby2JS with Stimulus Webpacker support"
|
@@ -34,6 +36,18 @@ namespace :ruby2js do
|
|
34
36
|
template 'install/stimulus-webpacker.rb'
|
35
37
|
end
|
36
38
|
end
|
39
|
+
|
40
|
+
namespace :lit do
|
41
|
+
desc "Install Ruby2JS with Lit Rollup support"
|
42
|
+
task :rollup do
|
43
|
+
template 'install/lit-rollup.rb'
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Install Ruby2JS with Lit Webpacker support"
|
47
|
+
task :webpacker do
|
48
|
+
template 'install/lit-webpacker.rb'
|
49
|
+
end
|
50
|
+
end
|
37
51
|
end
|
38
52
|
end
|
39
53
|
|
data/ruby2js.gemspec
CHANGED
@@ -12,12 +12,14 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.authors = ["Sam Ruby".freeze, "Jared White".freeze]
|
13
13
|
s.description = " The base package maps Ruby syntax to JavaScript semantics.\n Filters may be provided to add Ruby-specific or framework specific\n behavior.\n".freeze
|
14
14
|
s.email = "rubys@intertwingly.net".freeze
|
15
|
-
s.files = %w(ruby2js.gemspec README.md) + Dir.glob("{lib}/**/*")
|
15
|
+
s.files = %w(ruby2js.gemspec README.md bin/ruby2js demo/ruby2js.rb) + Dir.glob("{lib}/**/*")
|
16
16
|
s.homepage = "http://github.com/rubys/ruby2js".freeze
|
17
17
|
s.licenses = ["MIT".freeze]
|
18
18
|
s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze)
|
19
19
|
s.summary = "Minimal yet extensible Ruby to JavaScript conversion.".freeze
|
20
20
|
|
21
|
+
s.executables << 'ruby2js'
|
22
|
+
|
21
23
|
s.add_dependency('parser')
|
22
24
|
s.add_dependency('regexp_parser', '~> 2.1.1')
|
23
25
|
end
|