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
@@ -69,16 +69,16 @@ module Ruby2JS
|
|
69
69
|
left = rewrite(*left.children)
|
70
70
|
end
|
71
71
|
|
72
|
-
if right.type != :send
|
72
|
+
if right.type != :send or OPERATORS.flatten.include? right.children[1]
|
73
73
|
s(:and, left, right)
|
74
74
|
elsif conditionally_equals(left, right.children.first)
|
75
75
|
# a && a.b => a&.b
|
76
|
-
right.updated(:csend, [left, right.children
|
76
|
+
right.updated(:csend, [left, *right.children[1..-1]])
|
77
77
|
elsif conditionally_equals(left.children.last, right.children.first)
|
78
78
|
# a && b && b.c => a && b&.c
|
79
79
|
left.updated(:and, [left.children.first,
|
80
80
|
left.children.last.updated(:csend,
|
81
|
-
[left.children.last, right.children
|
81
|
+
[left.children.last, *right.children[1..-1]])])
|
82
82
|
else
|
83
83
|
s(:and, left, right)
|
84
84
|
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, :taglit, :gvar ]
|
18
|
+
:op_asgn, :and_asgn, :or_asgn, :taglit, :gvar, :csend ]
|
19
19
|
|
20
20
|
handle :autoreturn do |*statements|
|
21
21
|
return if statements == [nil]
|
data/lib/ruby2js/converter.rb
CHANGED
@@ -17,8 +17,8 @@ module Ruby2JS
|
|
17
17
|
|
18
18
|
LOGICAL = :and, :not, :or
|
19
19
|
OPERATORS = [:[], :[]=], [:not, :!], [:**], [:*, :/, :%], [:+, :-],
|
20
|
-
[:>>, :<<], [:&], [:^, :|], [:<=, :<, :>, :>=],
|
21
|
-
[:and, :or]
|
20
|
+
[:>>, :<<], [:&], [:^, :|], [:<=, :<, :>, :>=],
|
21
|
+
[:==, :!=, :===, :"!==", :=~, :!~], [:and, :or]
|
22
22
|
|
23
23
|
INVERT_OP = {
|
24
24
|
:< => :>=,
|
data/lib/ruby2js/demo.rb
CHANGED
@@ -3,6 +3,34 @@ require 'ruby2js'
|
|
3
3
|
|
4
4
|
module Ruby2JS
|
5
5
|
module Demo
|
6
|
+
# convert {"[:foo, :Bar]" => "wee"} to {[:foo, :Bar] => "wee"}
|
7
|
+
def self.parse_stringified_symbol_keys(mapping_hash)
|
8
|
+
updated_mappings = {}
|
9
|
+
|
10
|
+
mapping_hash.each do |k, v|
|
11
|
+
next updated_mappings[k] = v unless k.is_a?(String) && k.start_with?("[:")
|
12
|
+
|
13
|
+
new_k = k.tr("[]", "").split(",").map! {|str| str.strip.delete_prefix(":").to_sym }.map(&:to_sym)
|
14
|
+
updated_mappings[new_k] = v
|
15
|
+
end
|
16
|
+
|
17
|
+
updated_mappings
|
18
|
+
end
|
19
|
+
|
20
|
+
# convert {"Foo" => "[:bar, :Baz]"} to {"Foo" => [:bar, :Baz]}
|
21
|
+
def self.parse_stringified_symbol_values(mapping_hash)
|
22
|
+
updated_mappings = {}
|
23
|
+
|
24
|
+
mapping_hash.each do |k, v|
|
25
|
+
next updated_mappings[k] = v unless v.is_a?(String) && v.start_with?("[:")
|
26
|
+
|
27
|
+
new_v = v.tr("[]", "").split(",").map! {|str| str.strip.delete_prefix(":").to_sym }.map(&:to_sym)
|
28
|
+
updated_mappings[k] = new_v
|
29
|
+
end
|
30
|
+
|
31
|
+
updated_mappings
|
32
|
+
end
|
33
|
+
|
6
34
|
def self.parse_autoimports(mappings)
|
7
35
|
autoimports = {}
|
8
36
|
|
@@ -7,7 +7,6 @@ module Ruby2JS
|
|
7
7
|
|
8
8
|
def on_send(node)
|
9
9
|
target, method, *args = node.children
|
10
|
-
return super unless args.empty?
|
11
10
|
|
12
11
|
if es2015 and method == :blank?
|
13
12
|
create_or_update_import("blank$")
|
@@ -18,6 +17,15 @@ module Ruby2JS
|
|
18
17
|
elsif es2015 and method == :presence
|
19
18
|
create_or_update_import("presence$")
|
20
19
|
process node.updated :send, [nil, "presence$", target]
|
20
|
+
elsif es2015 and method == :chomp
|
21
|
+
create_or_update_import("chomp$")
|
22
|
+
process node.updated :send, [nil, "chomp$", target, *args]
|
23
|
+
elsif es2015 and method == :delete_prefix
|
24
|
+
create_or_update_import("deletePrefix$")
|
25
|
+
process node.updated :send, [nil, "deletePrefix$", target, *args]
|
26
|
+
elsif es2015 and method == :delete_suffix
|
27
|
+
create_or_update_import("deleteSuffix$")
|
28
|
+
process node.updated :send, [nil, "deleteSuffix$", target, *args]
|
21
29
|
else
|
22
30
|
super
|
23
31
|
end
|
@@ -69,7 +69,7 @@ module Ruby2JS
|
|
69
69
|
def handle_generic_node(node, node_type)
|
70
70
|
return node if node.type != node_type
|
71
71
|
|
72
|
-
if node.children[0] =~ /_
|
72
|
+
if node.children[0] =~ /_.*[?!\w]$/ and !ALLOWLIST.include?(node.children[0].to_s)
|
73
73
|
S(node_type , camelCase(node.children[0]), *node.children[1..-1])
|
74
74
|
else
|
75
75
|
node
|
@@ -136,7 +136,7 @@ module Ruby2JS
|
|
136
136
|
node = super
|
137
137
|
return node if node.type != :defs
|
138
138
|
|
139
|
-
if node.children[1] =~ /_
|
139
|
+
if node.children[1] =~ /_.*[?!\w]$/
|
140
140
|
S(:defs , node.children[0],
|
141
141
|
camelCase(node.children[1]), *node.children[2..-1])
|
142
142
|
else
|
data/lib/ruby2js/filter/esm.rb
CHANGED
@@ -13,16 +13,30 @@ module Ruby2JS
|
|
13
13
|
@esm_autoimports = options[:autoimports]
|
14
14
|
@esm_defs = options[:defs] || {}
|
15
15
|
@esm_explicit_tokens = Set.new
|
16
|
+
@esm_top = nil
|
17
|
+
|
18
|
+
# don't convert requires if Require filter is included
|
19
|
+
filters = options[:filters] || Filter::DEFAULTS
|
20
|
+
if \
|
21
|
+
defined? Ruby2JS::Filter::Require and
|
22
|
+
filters.include? Ruby2JS::Filter::Require
|
23
|
+
then
|
24
|
+
@esm_top = []
|
25
|
+
end
|
16
26
|
end
|
17
27
|
|
18
28
|
def process(node)
|
19
|
-
return super
|
29
|
+
return super if @esm_top
|
20
30
|
|
21
31
|
list = [node]
|
22
32
|
while list.length == 1 and list.first.type == :begin
|
23
33
|
list = list.first.children.dup
|
24
34
|
end
|
25
35
|
|
36
|
+
@esm_top = list
|
37
|
+
|
38
|
+
return super unless @esm_autoexports
|
39
|
+
|
26
40
|
replaced = []
|
27
41
|
list.map! do |child|
|
28
42
|
replacement = child
|
@@ -84,7 +98,7 @@ module Ruby2JS
|
|
84
98
|
target, method, *args = node.children
|
85
99
|
return super unless target.nil?
|
86
100
|
|
87
|
-
if method == :import
|
101
|
+
if method == :import or (method == :require and @esm_top&.include? @ast)
|
88
102
|
# don't do the conversion if the word import is followed by a paren
|
89
103
|
if node.loc.respond_to? :selector
|
90
104
|
selector = node.loc.selector
|
@@ -98,7 +112,7 @@ module Ruby2JS
|
|
98
112
|
# => import "file.css"
|
99
113
|
s(:import, args[0].children[0])
|
100
114
|
elsif args.length == 1 and \
|
101
|
-
|
115
|
+
args[0].type == :send and \
|
102
116
|
args[0].children[0].nil? and \
|
103
117
|
args[0].children[2].type == :send and \
|
104
118
|
args[0].children[2].children[0].nil? and \
|
@@ -152,17 +166,17 @@ module Ruby2JS
|
|
152
166
|
|
153
167
|
values = @esm_defs[node.children.last]
|
154
168
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
169
|
+
if values
|
170
|
+
values = values.map {|value|
|
171
|
+
if value.to_s.start_with? "@"
|
172
|
+
[value.to_s[1..-1].to_sym, s(:self)]
|
173
|
+
else
|
174
|
+
[value.to_sym, s(:autobind, s(:self))]
|
175
|
+
end
|
176
|
+
}.to_h
|
177
|
+
|
178
|
+
@namespace.defineProps values, [node.children.last]
|
179
|
+
end
|
166
180
|
end
|
167
181
|
|
168
182
|
super
|
@@ -341,7 +341,11 @@ module Ruby2JS
|
|
341
341
|
|
342
342
|
elsif method == :last
|
343
343
|
if node.children.length == 2
|
344
|
-
|
344
|
+
if es2022
|
345
|
+
process S(:send, target, :at, s(:int, -1))
|
346
|
+
else
|
347
|
+
process on_send S(:send, target, :[], s(:int, -1))
|
348
|
+
end
|
345
349
|
elsif node.children.length == 3
|
346
350
|
process S(:send, target, :slice,
|
347
351
|
s(:send, s(:attr, target, :length), :-, node.children[2]),
|
@@ -358,8 +362,12 @@ module Ruby2JS
|
|
358
362
|
# resolve negative literal indexes
|
359
363
|
i = proc do |index|
|
360
364
|
if index.type == :int and index.children.first < 0
|
361
|
-
|
362
|
-
|
365
|
+
if es2022
|
366
|
+
return process S(:send, target, :at, index)
|
367
|
+
else
|
368
|
+
process S(:send, S(:attr, target, :length), :-,
|
369
|
+
s(:int, -index.children.first))
|
370
|
+
end
|
363
371
|
else
|
364
372
|
index
|
365
373
|
end
|
@@ -1,218 +1,2 @@
|
|
1
|
-
|
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
|
-
return super unless node.children.length > 1
|
26
|
-
s(:send, s(:self), node.children.first.to_s[1..-1]+'=',
|
27
|
-
process(node.children[1]))
|
28
|
-
end
|
29
|
-
|
30
|
-
def on_op_asgn(node)
|
31
|
-
return super unless node.children.first.type == :ivasgn
|
32
|
-
var = node.children.first.children.first
|
33
|
-
return super unless @le_props&.include?(var)
|
34
|
-
super node.updated(nil, [s(:attr, s(:attr, nil, :this),
|
35
|
-
var.to_s[1..-1]), *node.children[1..-1]])
|
36
|
-
end
|
37
|
-
|
38
|
-
def on_class(node)
|
39
|
-
cname, inheritance, *body = node.children
|
40
|
-
return super unless inheritance == s(:const, nil, :LitElement)
|
41
|
-
|
42
|
-
@le_props = {}
|
43
|
-
le_walk(node)
|
44
|
-
|
45
|
-
prepend_list << LITELEMENT_IMPORT if modules_enabled?
|
46
|
-
|
47
|
-
nodes = body.dup
|
48
|
-
if nodes.length == 1 and nodes.first&.type == :begin
|
49
|
-
nodes = nodes.first.children.dup
|
50
|
-
end
|
51
|
-
|
52
|
-
# insert/update static get properties() {}
|
53
|
-
unless @le_props.empty?
|
54
|
-
values = nodes.find_index {|child|
|
55
|
-
child.type == :defs and child.children[0..1] == [s(:self), :properties]
|
56
|
-
}
|
57
|
-
|
58
|
-
if values == nil
|
59
|
-
nodes.unshift s(:defp, s(:self), :properties, s(:args), s(:return,
|
60
|
-
s(:hash, *@le_props.map {|name, type| s(:pair, s(:str, name.to_s[1..-1]),
|
61
|
-
s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String))))})))
|
62
|
-
elsif nodes[values].children[3].type == :hash
|
63
|
-
le_props = @le_props.map {|name, type|
|
64
|
-
[s(:sym, name.to_s[1..-1].to_sym),
|
65
|
-
s(:hash, s(:pair, s(:sym, :type), s(:const, nil, type || :String)))]
|
66
|
-
}.to_h.merge(
|
67
|
-
nodes[values].children[3].children.map {|pair| pair.children}.to_h
|
68
|
-
)
|
69
|
-
|
70
|
-
nodes[values] = nodes[values].updated(nil,
|
71
|
-
[*nodes[values].children[0..2], s(:hash,
|
72
|
-
*le_props.map{|name, value| s(:pair, name, value)})])
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# render of a string is converted to a taglit :html
|
77
|
-
render = nodes.find_index {|child|
|
78
|
-
child&.type == :def and child.children.first == :render
|
79
|
-
}
|
80
|
-
if render and %i[str dstr].include?(nodes[render].children[2]&.type)
|
81
|
-
nodes[render] = nodes[render].updated(:deff,
|
82
|
-
[*nodes[render].children[0..1],
|
83
|
-
s(:autoreturn, html_wrap(nodes[render].children[2]))])
|
84
|
-
end
|
85
|
-
|
86
|
-
# self.styles returning string is converted to a taglit :css
|
87
|
-
styles = nodes.find_index {|child|
|
88
|
-
child&.type == :defs and child.children[0..1] == [s(:self), :styles]
|
89
|
-
}
|
90
|
-
if styles and %i[str dstr].include?(nodes[styles].children[3]&.type)
|
91
|
-
string = nodes[styles].children[3]
|
92
|
-
string = s(:dstr, string) if string.type == :str
|
93
|
-
children = string.children.dup
|
94
|
-
|
95
|
-
while children.length > 1 and children.last.type == :str and
|
96
|
-
children.last.children.last.strip == ''
|
97
|
-
children.pop
|
98
|
-
end
|
99
|
-
|
100
|
-
if children.last.type == :str
|
101
|
-
children << s(:str, children.pop.children.first.chomp)
|
102
|
-
end
|
103
|
-
|
104
|
-
nodes[styles] = nodes[styles].updated(nil,
|
105
|
-
[*nodes[styles].children[0..2],
|
106
|
-
s(:autoreturn, s(:taglit, s(:sym, :css),
|
107
|
-
s(:dstr, *children)))])
|
108
|
-
end
|
109
|
-
|
110
|
-
# insert super calls into initializer
|
111
|
-
initialize = nodes.find_index {|child|
|
112
|
-
child&.type == :def and child.children.first == :initialize
|
113
|
-
}
|
114
|
-
if initialize and nodes[initialize].children.length == 3
|
115
|
-
statements = nodes[initialize].children[2..-1]
|
116
|
-
|
117
|
-
if statements.length == 1 and statements.first.type == :begin
|
118
|
-
statements = statements.first.children
|
119
|
-
end
|
120
|
-
|
121
|
-
unless statements.any? {|statement| %i[super zuper].include? statement.type}
|
122
|
-
nodes[initialize] = nodes[initialize].updated(nil,
|
123
|
-
[*nodes[initialize].children[0..1],
|
124
|
-
s(:begin, s(:zsuper), *statements)])
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
props = @le_props.keys.map {|prop| [prop.to_sym, s(:self)]}.to_h
|
129
|
-
|
130
|
-
nodes.unshift s(:defineProps, props)
|
131
|
-
|
132
|
-
nodes.pop unless nodes.last
|
133
|
-
|
134
|
-
node.updated(nil, [*node.children[0..1], s(:begin, *process_all(nodes))])
|
135
|
-
ensure
|
136
|
-
@le_props = nil
|
137
|
-
end
|
138
|
-
|
139
|
-
def html_wrap(node)
|
140
|
-
if node.type == :str and node.children.first.strip.start_with? '<'
|
141
|
-
s(:taglit, s(:sym, :html), s(:dstr, node))
|
142
|
-
elsif node.type == :dstr
|
143
|
-
prefix = ''
|
144
|
-
node.children.each do |child|
|
145
|
-
break unless child.type == :str
|
146
|
-
prefix += child.children.first
|
147
|
-
end
|
148
|
-
|
149
|
-
return node unless prefix.strip.start_with? '<'
|
150
|
-
|
151
|
-
children = node.children.map do |child|
|
152
|
-
if child.type == :str
|
153
|
-
child
|
154
|
-
else
|
155
|
-
html_wrap(child)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
while children.length > 1 and children.last.type == :str and
|
160
|
-
children.last.children.last.strip == ''
|
161
|
-
children.pop
|
162
|
-
end
|
163
|
-
|
164
|
-
if children.last.type == :str
|
165
|
-
children << s(:str, children.pop.children.first.chomp)
|
166
|
-
end
|
167
|
-
|
168
|
-
s(:taglit, s(:sym, :html), node.updated(nil, children))
|
169
|
-
elsif node.type == :begin
|
170
|
-
node.updated(nil, node.children.map {|child| html_wrap(child)})
|
171
|
-
elsif node.type == :if
|
172
|
-
node.updated(nil, [node.children.first,
|
173
|
-
*node.children[1..2].map {|child| html_wrap(child)}])
|
174
|
-
elsif node.type == :block and
|
175
|
-
node.children.first.children[1] == :map
|
176
|
-
node.updated(nil, [*node.children[0..1],
|
177
|
-
html_wrap(node.children[2])])
|
178
|
-
else
|
179
|
-
node
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
# analyze ivar usage
|
184
|
-
def le_walk(node)
|
185
|
-
node.children.each do |child|
|
186
|
-
next unless child.is_a? Parser::AST::Node
|
187
|
-
|
188
|
-
if child.type == :ivar
|
189
|
-
@le_props[child.children.first] ||= nil
|
190
|
-
elsif child.type == :ivasgn || child.type == :op_asgn
|
191
|
-
prop = child.children.first
|
192
|
-
unless prop.is_a? Symbol
|
193
|
-
prop = prop.children.first if prop.type == :ivasgn
|
194
|
-
next unless prop.is_a? Symbol
|
195
|
-
end
|
196
|
-
|
197
|
-
@le_props[prop] = case child.children.last.type
|
198
|
-
when :str, :dstr
|
199
|
-
:String
|
200
|
-
when :array
|
201
|
-
:Array
|
202
|
-
when :int, :float
|
203
|
-
:Number
|
204
|
-
when :true, :false
|
205
|
-
:Boolean
|
206
|
-
else
|
207
|
-
@le_props[prop] || :Object
|
208
|
-
end
|
209
|
-
else
|
210
|
-
le_walk(child)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
DEFAULTS.push LitElement
|
217
|
-
end
|
218
|
-
end
|
1
|
+
# for backwards compatibility
|
2
|
+
require_relative './lit'
|