ruby2js 3.6.1 → 4.0.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/README.md +1 -7
- data/lib/ruby2js.rb +32 -9
- data/lib/ruby2js/converter.rb +8 -2
- data/lib/ruby2js/converter/assign.rb +159 -0
- data/lib/ruby2js/converter/begin.rb +7 -2
- data/lib/ruby2js/converter/case.rb +7 -2
- data/lib/ruby2js/converter/class.rb +77 -21
- data/lib/ruby2js/converter/class2.rb +39 -11
- data/lib/ruby2js/converter/def.rb +6 -2
- data/lib/ruby2js/converter/dstr.rb +8 -3
- data/lib/ruby2js/converter/hash.rb +9 -5
- data/lib/ruby2js/converter/hide.rb +13 -0
- data/lib/ruby2js/converter/if.rb +10 -2
- data/lib/ruby2js/converter/import.rb +18 -3
- data/lib/ruby2js/converter/kwbegin.rb +9 -2
- data/lib/ruby2js/converter/literal.rb +2 -2
- data/lib/ruby2js/converter/module.rb +37 -5
- data/lib/ruby2js/converter/opasgn.rb +8 -0
- data/lib/ruby2js/converter/send.rb +41 -2
- data/lib/ruby2js/converter/vasgn.rb +5 -0
- data/lib/ruby2js/demo.rb +53 -0
- data/lib/ruby2js/es2022.rb +5 -0
- data/lib/ruby2js/es2022/strict.rb +3 -0
- data/lib/ruby2js/filter.rb +9 -1
- data/lib/ruby2js/filter/active_functions.rb +1 -0
- data/lib/ruby2js/filter/cjs.rb +2 -0
- data/lib/ruby2js/filter/esm.rb +44 -10
- data/lib/ruby2js/filter/functions.rb +84 -95
- data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
- data/lib/ruby2js/filter/react.rb +191 -56
- data/lib/ruby2js/filter/require.rb +100 -5
- data/lib/ruby2js/filter/return.rb +13 -1
- data/lib/ruby2js/filter/stimulus.rb +185 -0
- data/lib/ruby2js/jsx.rb +291 -0
- data/lib/ruby2js/namespace.rb +75 -0
- data/lib/ruby2js/version.rb +3 -3
- metadata +12 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 064d9dc5d65f6e2456e91d951e2f4311b776221422a77d59a8cfd466ceb5056d
|
4
|
+
data.tar.gz: ffc274a55ceb9a1280f776c00da8c296dc10672150174135931547cd056a3520
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fe9775803716fce25697adaf59a8afb57f4de499052a66847f18dfdd59c1f73b52d77be473adf762a57693a0896a6c90ed275a2b9152b5b6b548ca454e6d9ae
|
7
|
+
data.tar.gz: aa901eab47bba051a69a0ee132b69e7754aa5da49ff2e7e22560303a4c0fd02cdb727d4297c13098f3a6f82cf20788f4657c7ec21d145e66533875933d1a5154
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Documentation
|
|
13
13
|
|
14
14
|
* Visit **[ruby2js.com](https://www.ruby2js.com)** for detailed setup instructions and API reference.
|
15
15
|
|
16
|
-
* [Try Ruby2JS online](https://
|
16
|
+
* [Try Ruby2JS online](https://ruby2js.com/demo)
|
17
17
|
|
18
18
|
|
19
19
|
Synopsis
|
@@ -39,12 +39,6 @@ Host variable substitution:
|
|
39
39
|
puts Ruby2JS.convert("@name", ivars: {:@name => "Joe"})
|
40
40
|
```
|
41
41
|
|
42
|
-
Host expression evalution -- potentially unsafe, use only if you trust
|
43
|
-
the source being converted::
|
44
|
-
```ruby
|
45
|
-
i = 7; puts Ruby2JS.convert("i = `i`", binding: binding)
|
46
|
-
```
|
47
|
-
|
48
42
|
Enable ES2015 support:
|
49
43
|
|
50
44
|
```ruby
|
data/lib/ruby2js.rb
CHANGED
@@ -10,9 +10,15 @@ end
|
|
10
10
|
|
11
11
|
require 'ruby2js/converter'
|
12
12
|
require 'ruby2js/filter'
|
13
|
+
require 'ruby2js/namespace'
|
13
14
|
|
14
15
|
module Ruby2JS
|
15
16
|
class SyntaxError < RuntimeError
|
17
|
+
attr_reader :diagnostic
|
18
|
+
def initialize(message, diagnostic=nil)
|
19
|
+
super(message)
|
20
|
+
@diagnostic = diagnostic
|
21
|
+
end
|
16
22
|
end
|
17
23
|
|
18
24
|
@@eslevel_default = 2009 # ecmascript 5
|
@@ -61,7 +67,7 @@ module Ruby2JS
|
|
61
67
|
include Ruby2JS::Filter
|
62
68
|
BINARY_OPERATORS = Converter::OPERATORS[2..-1].flatten
|
63
69
|
|
64
|
-
attr_accessor :prepend_list, :disable_autoimports
|
70
|
+
attr_accessor :prepend_list, :disable_autoimports, :namespace
|
65
71
|
|
66
72
|
def initialize(comments)
|
67
73
|
@comments = comments
|
@@ -79,8 +85,8 @@ module Ruby2JS
|
|
79
85
|
def options=(options)
|
80
86
|
@options = options
|
81
87
|
|
82
|
-
@included =
|
83
|
-
@excluded =
|
88
|
+
@included = Filter.included_methods
|
89
|
+
@excluded = Filter.excluded_methods
|
84
90
|
|
85
91
|
include_all if options[:include_all]
|
86
92
|
include_only(options[:include_only]) if options[:include_only]
|
@@ -116,6 +122,10 @@ module Ruby2JS
|
|
116
122
|
@options[:eslevel] >= 2021
|
117
123
|
end
|
118
124
|
|
125
|
+
def es2022
|
126
|
+
@options[:eslevel] >= 2022
|
127
|
+
end
|
128
|
+
|
119
129
|
def process(node)
|
120
130
|
ast, @ast = @ast, node
|
121
131
|
replacement = super
|
@@ -130,6 +140,7 @@ module Ruby2JS
|
|
130
140
|
end
|
131
141
|
|
132
142
|
# handle all of the 'invented/synthetic' ast types
|
143
|
+
def on_assign(node); end
|
133
144
|
def on_async(node); on_def(node); end
|
134
145
|
def on_asyncs(node); on_defs(node); end
|
135
146
|
def on_attr(node); on_send(node); end
|
@@ -137,18 +148,23 @@ module Ruby2JS
|
|
137
148
|
def on_await(node); on_send(node); end
|
138
149
|
def on_call(node); on_send(node); end
|
139
150
|
def on_class_extend(node); on_send(node); end
|
151
|
+
def on_class_hash(node); on_class(node); end
|
140
152
|
def on_class_module(node); on_send(node); end
|
141
153
|
def on_constructor(node); on_def(node); end
|
154
|
+
def on_deff(node); on_def(node); end
|
142
155
|
def on_defm(node); on_defs(node); end
|
143
156
|
def on_defp(node); on_defs(node); end
|
144
157
|
def on_for_of(node); on_for(node); end
|
145
158
|
def on_in?(node); on_send(node); end
|
146
159
|
def on_method(node); on_send(node); end
|
160
|
+
def on_module_hash(node); on_module(node); end
|
147
161
|
def on_prop(node); on_array(node); end
|
148
162
|
def on_prototype(node); on_begin(node); end
|
149
163
|
def on_send!(node); on_send(node); end
|
150
164
|
def on_sendw(node); on_send(node); end
|
151
165
|
def on_undefined?(node); on_defined?(node); end
|
166
|
+
def on_defineProps(node); end
|
167
|
+
def on_hide(node); on_begin(node); end
|
152
168
|
def on_nil(node); end
|
153
169
|
def on_xnode(node); end
|
154
170
|
def on_export(node); end
|
@@ -178,6 +194,7 @@ module Ruby2JS
|
|
178
194
|
end
|
179
195
|
|
180
196
|
def self.convert(source, options={})
|
197
|
+
options = options.dup
|
181
198
|
options[:eslevel] ||= @@eslevel_default
|
182
199
|
options[:strict] = @@strict_default if options[:strict] == nil
|
183
200
|
options[:module] ||= @@module_default || :esm
|
@@ -197,6 +214,8 @@ module Ruby2JS
|
|
197
214
|
comments = ast ? Parser::Source::Comment.associate(ast, comments) : {}
|
198
215
|
end
|
199
216
|
|
217
|
+
namespace = Namespace.new
|
218
|
+
|
200
219
|
filters = (options[:filters] || Filter::DEFAULTS)
|
201
220
|
|
202
221
|
unless filters.empty?
|
@@ -211,6 +230,7 @@ module Ruby2JS
|
|
211
230
|
filter = filter.new(comments)
|
212
231
|
|
213
232
|
filter.options = options
|
233
|
+
filter.namespace = namespace
|
214
234
|
ast = filter.process(ast)
|
215
235
|
|
216
236
|
unless filter.prepend_list.empty?
|
@@ -229,7 +249,10 @@ module Ruby2JS
|
|
229
249
|
ruby2js.comparison = options[:comparison] || :equality
|
230
250
|
ruby2js.or = options[:or] || :logical
|
231
251
|
ruby2js.module_type = options[:module] || :esm
|
232
|
-
ruby2js.underscored_private = (options[:eslevel] <
|
252
|
+
ruby2js.underscored_private = (options[:eslevel] < 2022) || options[:underscored_private]
|
253
|
+
|
254
|
+
ruby2js.namespace = namespace
|
255
|
+
|
233
256
|
if ruby2js.binding and not ruby2js.ivars
|
234
257
|
ruby2js.ivars = ruby2js.binding.eval \
|
235
258
|
'Hash[instance_variables.map {|var| [var, instance_variable_get(var)]}]'
|
@@ -256,16 +279,16 @@ module Ruby2JS
|
|
256
279
|
buffer = Parser::Source::Buffer.new(file, line)
|
257
280
|
buffer.source = source.encode('utf-8')
|
258
281
|
parser = Parser::CurrentRuby.new
|
259
|
-
parser.
|
260
|
-
|
261
|
-
|
262
|
-
|
282
|
+
parser.diagnostics.all_errors_are_fatal = true
|
283
|
+
parser.diagnostics.consumer = lambda {|diagnostic| nil}
|
284
|
+
parser.builder.emit_file_line_as_literals = false
|
285
|
+
parser.parse_with_comments(buffer)
|
263
286
|
rescue Parser::SyntaxError => e
|
264
287
|
split = source[0..e.diagnostic.location.begin_pos].split("\n")
|
265
288
|
line, col = split.length, split.last.length
|
266
289
|
message = "line #{line}, column #{col}: #{e.diagnostic.message}"
|
267
290
|
message += "\n in file #{file}" if file
|
268
|
-
raise Ruby2JS::SyntaxError.new(message)
|
291
|
+
raise Ruby2JS::SyntaxError.new(message, e.diagnostic)
|
269
292
|
end
|
270
293
|
|
271
294
|
def self.find_block(ast, line)
|
data/lib/ruby2js/converter.rb
CHANGED
@@ -14,7 +14,7 @@ module Ruby2JS
|
|
14
14
|
|
15
15
|
class Converter < Serializer
|
16
16
|
attr_accessor :ast
|
17
|
-
|
17
|
+
|
18
18
|
LOGICAL = :and, :not, :or
|
19
19
|
OPERATORS = [:[], :[]=], [:not, :!], [:**], [:*, :/, :%], [:+, :-],
|
20
20
|
[:>>, :<<], [:&], [:^, :|], [:<=, :<, :>, :>=], [:==, :!=, :===, :"!=="],
|
@@ -34,7 +34,7 @@ module Ruby2JS
|
|
34
34
|
|
35
35
|
VASGN = [:cvasgn, :ivasgn, :gvasgn, :lvasgn]
|
36
36
|
|
37
|
-
attr_accessor :binding, :ivars
|
37
|
+
attr_accessor :binding, :ivars, :namespace
|
38
38
|
|
39
39
|
def initialize( ast, comments, vars = {} )
|
40
40
|
super()
|
@@ -160,6 +160,10 @@ module Ruby2JS
|
|
160
160
|
@eslevel >= 2021
|
161
161
|
end
|
162
162
|
|
163
|
+
def es2022
|
164
|
+
@eslevel >= 2022
|
165
|
+
end
|
166
|
+
|
163
167
|
@@handlers = []
|
164
168
|
def self.handle(*types, &block)
|
165
169
|
types.each do |type|
|
@@ -295,6 +299,7 @@ end
|
|
295
299
|
require 'ruby2js/converter/arg'
|
296
300
|
require 'ruby2js/converter/args'
|
297
301
|
require 'ruby2js/converter/array'
|
302
|
+
require 'ruby2js/converter/assign'
|
298
303
|
require 'ruby2js/converter/begin'
|
299
304
|
require 'ruby2js/converter/block'
|
300
305
|
require 'ruby2js/converter/blockpass'
|
@@ -314,6 +319,7 @@ require 'ruby2js/converter/dstr'
|
|
314
319
|
require 'ruby2js/converter/fileline'
|
315
320
|
require 'ruby2js/converter/for'
|
316
321
|
require 'ruby2js/converter/hash'
|
322
|
+
require 'ruby2js/converter/hide'
|
317
323
|
require 'ruby2js/converter/if'
|
318
324
|
require 'ruby2js/converter/in'
|
319
325
|
require 'ruby2js/converter/import'
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# Kinda like Object.assign, except it handles properties
|
2
|
+
#
|
3
|
+
# Note: Object.defineProperties, Object.getOwnPropertyNames, etc. technically
|
4
|
+
# were not part of ES5, but were implemented by IE prior to ES6, and are
|
5
|
+
# the only way to implement getters and setters.
|
6
|
+
|
7
|
+
module Ruby2JS
|
8
|
+
class Converter
|
9
|
+
|
10
|
+
# (assign
|
11
|
+
# target
|
12
|
+
# (hash)
|
13
|
+
# ...
|
14
|
+
|
15
|
+
handle :assign do |target, *args|
|
16
|
+
collapsible = false
|
17
|
+
|
18
|
+
nonprop = proc do |node|
|
19
|
+
next false unless node.is_a? Parser::AST::Node
|
20
|
+
next false if node.type == :pair and node.children.first.type == :prop and es2015
|
21
|
+
next true unless node.type == :def
|
22
|
+
next false if node.children.first.to_s.end_with? '='
|
23
|
+
node.is_method?
|
24
|
+
end
|
25
|
+
|
26
|
+
collapsible = true if args.length == 1 and args.first.type == :hash and
|
27
|
+
args.first.children.length == 1
|
28
|
+
|
29
|
+
collapsible = true if args.length == 1 and args.first.type == :class_module and
|
30
|
+
args.first.children.length == 3 and nonprop[args.first.children.last]
|
31
|
+
|
32
|
+
if es2015 and not collapsible and args.all? {|arg|
|
33
|
+
case arg.type
|
34
|
+
when :pair, :hash, :class_module
|
35
|
+
arg.children.all? {|child| nonprop[child]}
|
36
|
+
when :const
|
37
|
+
false
|
38
|
+
else
|
39
|
+
true
|
40
|
+
end
|
41
|
+
}
|
42
|
+
parse s(:send, s(:const, nil, :Object), :assign, target, *args)
|
43
|
+
else
|
44
|
+
|
45
|
+
if target == s(:hash)
|
46
|
+
copy = [s(:gvasgn, :$$, target)]
|
47
|
+
target = s(:gvar, :$$)
|
48
|
+
shadow = [s(:shadowarg, :$$)]
|
49
|
+
elsif collapsible or es2015 or
|
50
|
+
(%i(send const).include? target.type and
|
51
|
+
target.children.length == 2 and target.children[0] == nil)
|
52
|
+
then
|
53
|
+
copy = []
|
54
|
+
shadow = []
|
55
|
+
else
|
56
|
+
copy = [s(:gvasgn, :$0, target)]
|
57
|
+
target = s(:gvar, :$0)
|
58
|
+
shadow = [s(:shadowarg, :$0)]
|
59
|
+
end
|
60
|
+
|
61
|
+
body = [*copy,
|
62
|
+
*args.map {|modname|
|
63
|
+
if modname.type == :hash and
|
64
|
+
modname.children.all? {|pair| pair.children.first.type == :prop}
|
65
|
+
|
66
|
+
if modname.children.length == 1
|
67
|
+
pair = modname.children.first
|
68
|
+
s(:send, s(:const, nil, :Object), :defineProperty, target,
|
69
|
+
s(:sym, pair.children.first.children.last),
|
70
|
+
s(:hash, *pair.children.last.map {|name, value| s(:pair,
|
71
|
+
s(:sym, name), value)}))
|
72
|
+
else
|
73
|
+
pair = modname.children.first
|
74
|
+
s(:send, s(:const, nil, :Object), :defineProperties, target,
|
75
|
+
s(:hash, *modname.children.map {|pair| s(:pair,
|
76
|
+
s(:sym, pair.children.first.children.last),
|
77
|
+
s(:hash, *pair.children.last.map {|name, value| s(:pair,
|
78
|
+
s(:sym, name), value)})
|
79
|
+
)}))
|
80
|
+
end
|
81
|
+
|
82
|
+
elsif modname.type == :hash and
|
83
|
+
modname.children.all? {|child| nonprop[child]}
|
84
|
+
|
85
|
+
s(:begin, *modname.children.map {|pair|
|
86
|
+
if pair.children.first.type == :prop
|
87
|
+
s(:send, s(:const, nil, :Object), :defineProperty, target,
|
88
|
+
s(:sym, pair.children.first.children.last),
|
89
|
+
s(:hash, *pair.children.last.map {|name, value| s(:pair,
|
90
|
+
s(:sym, name), value)}))
|
91
|
+
else
|
92
|
+
s(:send, target, :[]=, *pair.children)
|
93
|
+
end
|
94
|
+
})
|
95
|
+
|
96
|
+
elsif modname.type == :class_module and
|
97
|
+
modname.children[2..-1].all? {|child| nonprop[child]}
|
98
|
+
|
99
|
+
s(:begin, *modname.children[2..-1].map {|pair|
|
100
|
+
s(:send, target, :[]=, s(:sym, pair.children.first),
|
101
|
+
pair.updated(:defm, [nil, *pair.children[1..-1]]))
|
102
|
+
})
|
103
|
+
|
104
|
+
elsif modname.type == :lvar and not es2015
|
105
|
+
s(:for, s(:lvasgn, :$_), modname,
|
106
|
+
s(:send, target, :[]=,
|
107
|
+
s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
|
108
|
+
|
109
|
+
else
|
110
|
+
if es2017
|
111
|
+
s(:send, s(:const, nil, :Object), :defineProperties, target,
|
112
|
+
s(:send, s(:const, nil, :Object), :getOwnPropertyDescriptors, modname))
|
113
|
+
else
|
114
|
+
if modname.type == :lvar or (%i(send const).include? modname.type and
|
115
|
+
modname.children.length == 2 and modname.children[0] == nil)
|
116
|
+
|
117
|
+
object = modname
|
118
|
+
else
|
119
|
+
shadow += [s(:shadowarg, :$1)]
|
120
|
+
object = s(:gvar, :$1)
|
121
|
+
end
|
122
|
+
|
123
|
+
copy = s(:send,
|
124
|
+
s(:const, nil, :Object), :defineProperties, target,
|
125
|
+
s(:send,
|
126
|
+
s(:send, s(:const, nil, :Object), :getOwnPropertyNames, object),
|
127
|
+
:reduce,
|
128
|
+
s(:block,
|
129
|
+
s(:send, nil, :lambda),
|
130
|
+
s(:args, s(:arg, :$2), s(:arg, :$3)),
|
131
|
+
s(:begin,
|
132
|
+
s(:send,
|
133
|
+
s(:lvar, :$2), :[]=, s(:lvar, :$3),
|
134
|
+
s(:send, s(:const, nil, :Object), :getOwnPropertyDescriptor,
|
135
|
+
object, s(:lvar, :$3))),
|
136
|
+
s(:return, s(:lvar, :$2)))),
|
137
|
+
s(:hash)))
|
138
|
+
|
139
|
+
|
140
|
+
if object.type == :gvar
|
141
|
+
s(:begin, s(:gvasgn, object.children.last, modname), copy)
|
142
|
+
else
|
143
|
+
copy
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
}]
|
148
|
+
|
149
|
+
if @state == :statement and shadow.empty?
|
150
|
+
parse s(:begin, *body)
|
151
|
+
else
|
152
|
+
body.push s(:return, target) if @state == :expression
|
153
|
+
parse s(:send, s(:block, s(:send, nil, :lambda), s(:args, *shadow),
|
154
|
+
s(:begin, *body)), :[])
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -9,6 +9,11 @@ module Ruby2JS
|
|
9
9
|
state = @state
|
10
10
|
props = false
|
11
11
|
|
12
|
+
if state == :expression and statements.empty?
|
13
|
+
puts 'null'
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
12
17
|
statements.map! do |statement|
|
13
18
|
case statement and statement.type
|
14
19
|
when :defs, :defp
|
@@ -32,9 +37,9 @@ module Ruby2JS
|
|
32
37
|
end
|
33
38
|
|
34
39
|
def combine_properties(body)
|
35
|
-
|
40
|
+
(0...body.length-1).each do |i|
|
36
41
|
next unless body[i] and body[i].type == :prop
|
37
|
-
|
42
|
+
(i+1...body.length).each do |j|
|
38
43
|
break unless body[j] and body[j].type == :prop
|
39
44
|
|
40
45
|
if body[i].children[0] == body[j].children[0]
|
@@ -10,10 +10,15 @@ module Ruby2JS
|
|
10
10
|
|
11
11
|
handle :case do |expr, *whens, other|
|
12
12
|
begin
|
13
|
+
if @state == :expression
|
14
|
+
parse s(:kwbegin, @ast), @state
|
15
|
+
return
|
16
|
+
end
|
17
|
+
|
13
18
|
inner, @inner = @inner, @ast
|
14
19
|
|
15
20
|
has_range = whens.any? do |node|
|
16
|
-
node.children.any? {|child| [:irange, :erange].include? child
|
21
|
+
node.children.any? {|child| [:irange, :erange].include? child&.type}
|
17
22
|
end
|
18
23
|
|
19
24
|
if has_range
|
@@ -47,7 +52,7 @@ module Ruby2JS
|
|
47
52
|
|
48
53
|
parse code, :statement
|
49
54
|
last = code
|
50
|
-
while last
|
55
|
+
while last&.type == :begin
|
51
56
|
last = last.children.last
|
52
57
|
end
|
53
58
|
|
@@ -11,13 +11,27 @@ module Ruby2JS
|
|
11
11
|
# NOTE: class_extend is not generated by the parser, but instead produced
|
12
12
|
# when ++class is encountered; it signals that this construct is
|
13
13
|
# meant to extend an already existing JavaScrpt class.
|
14
|
+
#
|
15
|
+
# class_hash is an anonymous class as a value in a hash; the
|
16
|
+
# name has already been output so should be ignored other than
|
17
|
+
# in determining the namespace.
|
18
|
+
#
|
19
|
+
# class_module is a module that to be re-processed by this handler
|
20
|
+
# given the similarity between the two structures.
|
14
21
|
|
15
|
-
handle :class, :class_extend, :class_module do |name, inheritance, *body|
|
16
|
-
|
22
|
+
handle :class, :class_hash, :class_extend, :class_module do |name, inheritance, *body|
|
23
|
+
extend = @namespace.enter(name) unless @ast.type == :class_module
|
24
|
+
|
25
|
+
if !%i(class class_hash).include?(@ast.type) or extend
|
17
26
|
init = nil
|
18
27
|
else
|
19
|
-
if es2015
|
20
|
-
|
28
|
+
if es2015 and not extend
|
29
|
+
if @ast.type == :class_hash
|
30
|
+
parse @ast.updated(:class2, [nil, *@ast.children[1..-1]])
|
31
|
+
else
|
32
|
+
parse @ast.updated(:class2)
|
33
|
+
end
|
34
|
+
@namespace.leave unless @ast.type == :class_module
|
21
35
|
return
|
22
36
|
end
|
23
37
|
|
@@ -35,7 +49,7 @@ module Ruby2JS
|
|
35
49
|
end
|
36
50
|
|
37
51
|
body.compact!
|
38
|
-
visible =
|
52
|
+
visible = @namespace.getOwnProps
|
39
53
|
body.map! do |m|
|
40
54
|
if \
|
41
55
|
@ast.type == :class_module and m.type == :defs and
|
@@ -45,7 +59,7 @@ module Ruby2JS
|
|
45
59
|
end
|
46
60
|
|
47
61
|
node = if m.type == :def
|
48
|
-
if m.children.first == :initialize
|
62
|
+
if m.children.first == :initialize and !visible[:initialize]
|
49
63
|
# constructor: remove from body and overwrite init function
|
50
64
|
init = m
|
51
65
|
nil
|
@@ -54,17 +68,20 @@ module Ruby2JS
|
|
54
68
|
sym = :"#{m.children.first.to_s[0..-2]}"
|
55
69
|
s(:prop, s(:attr, name, :prototype), sym =>
|
56
70
|
{enumerable: s(:true), configurable: s(:true),
|
57
|
-
set: s(:
|
71
|
+
set: s(:defm, nil, *m.children[1..-1])})
|
58
72
|
else
|
59
|
-
visible[m.children[0]] = s(:self)
|
60
73
|
|
61
74
|
if not m.is_method?
|
75
|
+
visible[m.children[0]] = s(:self)
|
76
|
+
|
62
77
|
# property getter
|
63
78
|
s(:prop, s(:attr, name, :prototype), m.children.first =>
|
64
79
|
{enumerable: s(:true), configurable: s(:true),
|
65
|
-
get: s(:
|
80
|
+
get: s(:defm, nil, m.children[1],
|
66
81
|
m.updated(:autoreturn, m.children[2..-1]))})
|
67
82
|
else
|
83
|
+
visible[m.children[0]] = s(:autobind, s(:self))
|
84
|
+
|
68
85
|
# method: add to prototype
|
69
86
|
s(:method, s(:attr, name, :prototype),
|
70
87
|
:"#{m.children[0].to_s.chomp('!')}=",
|
@@ -90,7 +107,7 @@ module Ruby2JS
|
|
90
107
|
else
|
91
108
|
# class method definition: add to prototype
|
92
109
|
s(:prototype, s(:send, name, "#{m.children[1]}=",
|
93
|
-
s(:
|
110
|
+
s(:defm, nil, *m.children[2..-1])))
|
94
111
|
end
|
95
112
|
|
96
113
|
elsif m.type == :send and m.children.first == nil
|
@@ -125,9 +142,10 @@ module Ruby2JS
|
|
125
142
|
elsif m.children[1] == :include
|
126
143
|
s(:send, s(:block, s(:send, nil, :lambda), s(:args),
|
127
144
|
s(:begin, *m.children[2..-1].map {|modname|
|
128
|
-
|
129
|
-
|
130
|
-
|
145
|
+
@namespace.defineProps @namespace.find(modname)
|
146
|
+
s(:for, s(:lvasgn, :$_), modname,
|
147
|
+
s(:send, s(:attr, name, :prototype), :[]=,
|
148
|
+
s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
|
131
149
|
})), :[])
|
132
150
|
elsif [:private, :protected, :public].include? m.children[1]
|
133
151
|
raise Error.new("class #{m.children[1]} is not supported", @ast)
|
@@ -160,17 +178,23 @@ module Ruby2JS
|
|
160
178
|
s(:send, s(:attr, name, :prototype),
|
161
179
|
"#{m.children[0].children.first}=",
|
162
180
|
s(:attr, s(:attr, name, :prototype), m.children[1].children.first))
|
163
|
-
elsif m.type == :class
|
181
|
+
elsif m.type == :class or m.type == :module
|
164
182
|
innerclass_name = m.children.first
|
165
183
|
if innerclass_name.children.first
|
166
184
|
innerclass_name = innerclass_name.updated(nil,
|
167
|
-
[s(:attr, innerclass_name.children[0]
|
185
|
+
[s(:attr, name, innerclass_name.children[0].children.last),
|
168
186
|
innerclass_name.children[1]])
|
169
187
|
else
|
170
188
|
innerclass_name = innerclass_name.updated(nil,
|
171
189
|
[name, innerclass_name.children[1]])
|
172
190
|
end
|
173
191
|
m.updated(nil, [innerclass_name, *m.children[1..-1]])
|
192
|
+
elsif @ast.type == :class_module
|
193
|
+
m
|
194
|
+
elsif m.type == :defineProps
|
195
|
+
@namespace.defineProps m.children.first
|
196
|
+
visible.merge! m.children.first
|
197
|
+
nil
|
174
198
|
else
|
175
199
|
raise Error.new("class #{ m.type } not supported", @ast)
|
176
200
|
end
|
@@ -194,7 +218,7 @@ module Ruby2JS
|
|
194
218
|
# merge property definitions
|
195
219
|
combine_properties(body)
|
196
220
|
|
197
|
-
if inheritance
|
221
|
+
if inheritance and (@ast.type != :class_extend and !extend)
|
198
222
|
body.unshift s(:send, name, :prototype=,
|
199
223
|
s(:send, s(:const, nil, :Object), :create,
|
200
224
|
s(:attr, inheritance, :prototype))),
|
@@ -206,10 +230,14 @@ module Ruby2JS
|
|
206
230
|
methods = 0
|
207
231
|
start = 0
|
208
232
|
body.each do |node|
|
209
|
-
if
|
233
|
+
if (node.type == :method or (node.type == :prop and es2015)) and
|
210
234
|
node.children[0].type == :attr and
|
211
235
|
node.children[0].children[1] == :prototype
|
212
236
|
methods += 1
|
237
|
+
elsif node.type == :class and @ast.type == :class_module and es2015
|
238
|
+
methods += 1 if node.children.first.children.first == name
|
239
|
+
elsif node.type == :module and @ast.type == :class_module
|
240
|
+
methods += 1 if node.children.first.children.first == name
|
213
241
|
elsif methods == 0
|
214
242
|
start += 1
|
215
243
|
else
|
@@ -219,14 +247,22 @@ module Ruby2JS
|
|
219
247
|
|
220
248
|
# collapse sequence to a single assignment
|
221
249
|
if \
|
222
|
-
@ast.type
|
223
|
-
|
250
|
+
@ast.type == :class_module or methods > 1 or
|
251
|
+
body[start]&.type == :prop
|
224
252
|
then
|
225
253
|
pairs = body[start...start+methods].map do |node|
|
226
254
|
if node.type == :method
|
227
255
|
replacement = node.updated(:pair, [
|
228
256
|
s(:str, node.children[1].to_s.chomp('=')),
|
229
257
|
node.children[2]])
|
258
|
+
elsif node.type == :class and node.children.first.children.first == name
|
259
|
+
sym = node.children.first.children.last
|
260
|
+
replacement = s(:pair, s(:sym, sym),
|
261
|
+
s(:class_hash, s(:const, nil, sym), nil, node.children.last))
|
262
|
+
elsif node.type == :module and node.children.first.children.first == name
|
263
|
+
sym = node.children.first.children.last
|
264
|
+
replacement = s(:pair, s(:sym, sym),
|
265
|
+
s(:module_hash, s(:const, nil, sym), node.children.last))
|
230
266
|
else
|
231
267
|
replacement = node.children[1].map do |prop, descriptor|
|
232
268
|
node.updated(:pair, [s(:prop, prop), descriptor])
|
@@ -244,12 +280,30 @@ module Ruby2JS
|
|
244
280
|
end
|
245
281
|
|
246
282
|
if @ast.type == :class_module
|
283
|
+
start = 0 if methods == 0
|
284
|
+
if name
|
285
|
+
body[start...start+methods] =
|
286
|
+
s(:casgn, *name.children, s(:hash, *pairs.flatten))
|
287
|
+
else
|
288
|
+
body[start...start+methods] = s(:hash, *pairs.flatten)
|
289
|
+
end
|
290
|
+
elsif @ast.type == :class_extend or extend
|
247
291
|
body[start...start+methods] =
|
248
|
-
s(:
|
292
|
+
s(:assign, body[start].children.first, s(:hash, *pairs.flatten))
|
249
293
|
else
|
250
294
|
body[start...start+methods] =
|
251
295
|
s(:send, name, :prototype=, s(:hash, *pairs.flatten))
|
252
296
|
end
|
297
|
+
|
298
|
+
elsif (@ast.type == :class_extend or extend) and methods > 1
|
299
|
+
|
300
|
+
pairs = body[start...start+methods].map do |node|
|
301
|
+
node.updated(:pair, [
|
302
|
+
s(:sym, node.children[1].to_s[0..-2]), node.children[2]])
|
303
|
+
end
|
304
|
+
|
305
|
+
body[start...start+methods] =
|
306
|
+
s(:assign, body[start].children.first, s(:hash, *pairs))
|
253
307
|
end
|
254
308
|
end
|
255
309
|
|
@@ -258,7 +312,7 @@ module Ruby2JS
|
|
258
312
|
constructor = init.updated(:constructor, [name, *init.children[1..-1]])
|
259
313
|
@comments[constructor] = @comments[init] unless @comments[init].empty?
|
260
314
|
|
261
|
-
if @ast.type == :class_extend
|
315
|
+
if @ast.type == :class_extend or extend
|
262
316
|
if es2015
|
263
317
|
constructor = s(:masgn, s(:mlhs,
|
264
318
|
s(:attr, s(:casgn, *name.children, constructor), :prototype)),
|
@@ -285,6 +339,7 @@ module Ruby2JS
|
|
285
339
|
|
286
340
|
# add locally visible interfaces to rbstack. See send.rb, const.rb
|
287
341
|
@rbstack.push visible
|
342
|
+
@rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
|
288
343
|
|
289
344
|
parse s(:begin, *body.compact), :statement
|
290
345
|
ensure
|
@@ -292,6 +347,7 @@ module Ruby2JS
|
|
292
347
|
@class_name = class_name
|
293
348
|
@class_parent = class_parent
|
294
349
|
@rbstack.pop
|
350
|
+
@namespace.leave unless @ast.type == :class_module
|
295
351
|
end
|
296
352
|
end
|
297
353
|
|