ruby2js 3.5.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -662
- data/lib/ruby2js.rb +61 -10
- data/lib/ruby2js/converter.rb +10 -4
- 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 +102 -31
- data/lib/ruby2js/converter/def.rb +7 -3
- 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 +35 -4
- data/lib/ruby2js/converter/kwbegin.rb +9 -2
- data/lib/ruby2js/converter/literal.rb +14 -2
- data/lib/ruby2js/converter/module.rb +41 -4
- data/lib/ruby2js/converter/opasgn.rb +8 -0
- data/lib/ruby2js/converter/send.rb +45 -5
- data/lib/ruby2js/converter/vasgn.rb +5 -0
- data/lib/ruby2js/converter/xstr.rb +1 -1
- 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 +44 -0
- data/lib/ruby2js/filter/camelCase.rb +4 -3
- data/lib/ruby2js/filter/cjs.rb +2 -0
- data/lib/ruby2js/filter/esm.rb +133 -7
- data/lib/ruby2js/filter/functions.rb +107 -98
- data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
- data/lib/ruby2js/filter/node.rb +95 -74
- data/lib/ruby2js/filter/nokogiri.rb +15 -41
- data/lib/ruby2js/filter/react.rb +191 -56
- data/lib/ruby2js/filter/require.rb +100 -5
- data/lib/ruby2js/filter/return.rb +15 -1
- data/lib/ruby2js/filter/securerandom.rb +33 -0
- data/lib/ruby2js/filter/stimulus.rb +185 -0
- data/lib/ruby2js/filter/vue.rb +9 -0
- data/lib/ruby2js/jsx.rb +291 -0
- data/lib/ruby2js/namespace.rb +75 -0
- data/lib/ruby2js/rails.rb +15 -9
- data/lib/ruby2js/serializer.rb +3 -1
- data/lib/ruby2js/version.rb +3 -3
- data/ruby2js.gemspec +1 -1
- metadata +14 -5
- data/lib/ruby2js/filter/esm_migration.rb +0 -72
- data/lib/ruby2js/filter/fast-deep-equal.rb +0 -23
data/lib/ruby2js.rb
CHANGED
@@ -10,13 +10,20 @@ 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
|
19
25
|
@@strict_default = false
|
26
|
+
@@module_default = nil
|
20
27
|
|
21
28
|
def self.eslevel_default
|
22
29
|
@@eslevel_default
|
@@ -34,6 +41,14 @@ module Ruby2JS
|
|
34
41
|
@@strict_default = level
|
35
42
|
end
|
36
43
|
|
44
|
+
def self.module_default
|
45
|
+
@@module_default
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.module_default=(module_type)
|
49
|
+
@@module_default = module_type
|
50
|
+
end
|
51
|
+
|
37
52
|
module Filter
|
38
53
|
DEFAULTS = []
|
39
54
|
|
@@ -52,18 +67,26 @@ module Ruby2JS
|
|
52
67
|
include Ruby2JS::Filter
|
53
68
|
BINARY_OPERATORS = Converter::OPERATORS[2..-1].flatten
|
54
69
|
|
70
|
+
attr_accessor :prepend_list, :disable_autoimports, :namespace
|
71
|
+
|
55
72
|
def initialize(comments)
|
56
73
|
@comments = comments
|
74
|
+
|
75
|
+
# check if magic comment is present:
|
76
|
+
first_comment = @comments.values.first&.map(&:text)&.first
|
77
|
+
@disable_autoimports = first_comment&.include?(" autoimports: false")
|
78
|
+
@disable_autoexports = first_comment&.include?(" autoexports: false")
|
79
|
+
|
57
80
|
@ast = nil
|
58
81
|
@exclude_methods = []
|
59
|
-
@
|
82
|
+
@prepend_list = Set.new
|
60
83
|
end
|
61
84
|
|
62
85
|
def options=(options)
|
63
86
|
@options = options
|
64
87
|
|
65
|
-
@included =
|
66
|
-
@excluded =
|
88
|
+
@included = Filter.included_methods
|
89
|
+
@excluded = Filter.excluded_methods
|
67
90
|
|
68
91
|
include_all if options[:include_all]
|
69
92
|
include_only(options[:include_only]) if options[:include_only]
|
@@ -99,6 +122,10 @@ module Ruby2JS
|
|
99
122
|
@options[:eslevel] >= 2021
|
100
123
|
end
|
101
124
|
|
125
|
+
def es2022
|
126
|
+
@options[:eslevel] >= 2022
|
127
|
+
end
|
128
|
+
|
102
129
|
def process(node)
|
103
130
|
ast, @ast = @ast, node
|
104
131
|
replacement = super
|
@@ -113,6 +140,7 @@ module Ruby2JS
|
|
113
140
|
end
|
114
141
|
|
115
142
|
# handle all of the 'invented/synthetic' ast types
|
143
|
+
def on_assign(node); end
|
116
144
|
def on_async(node); on_def(node); end
|
117
145
|
def on_asyncs(node); on_defs(node); end
|
118
146
|
def on_attr(node); on_send(node); end
|
@@ -120,17 +148,23 @@ module Ruby2JS
|
|
120
148
|
def on_await(node); on_send(node); end
|
121
149
|
def on_call(node); on_send(node); end
|
122
150
|
def on_class_extend(node); on_send(node); end
|
151
|
+
def on_class_hash(node); on_class(node); end
|
123
152
|
def on_class_module(node); on_send(node); end
|
124
153
|
def on_constructor(node); on_def(node); end
|
154
|
+
def on_deff(node); on_def(node); end
|
125
155
|
def on_defm(node); on_defs(node); end
|
126
156
|
def on_defp(node); on_defs(node); end
|
127
157
|
def on_for_of(node); on_for(node); end
|
128
158
|
def on_in?(node); on_send(node); end
|
129
159
|
def on_method(node); on_send(node); end
|
160
|
+
def on_module_hash(node); on_module(node); end
|
130
161
|
def on_prop(node); on_array(node); end
|
131
162
|
def on_prototype(node); on_begin(node); end
|
163
|
+
def on_send!(node); on_send(node); end
|
132
164
|
def on_sendw(node); on_send(node); end
|
133
165
|
def on_undefined?(node); on_defined?(node); end
|
166
|
+
def on_defineProps(node); end
|
167
|
+
def on_hide(node); on_begin(node); end
|
134
168
|
def on_nil(node); end
|
135
169
|
def on_xnode(node); end
|
136
170
|
def on_export(node); end
|
@@ -160,8 +194,10 @@ module Ruby2JS
|
|
160
194
|
end
|
161
195
|
|
162
196
|
def self.convert(source, options={})
|
197
|
+
options = options.dup
|
163
198
|
options[:eslevel] ||= @@eslevel_default
|
164
199
|
options[:strict] = @@strict_default if options[:strict] == nil
|
200
|
+
options[:module] ||= @@module_default || :esm
|
165
201
|
|
166
202
|
if Proc === source
|
167
203
|
file,line = source.source_location
|
@@ -175,9 +211,11 @@ module Ruby2JS
|
|
175
211
|
source = ast.loc.expression.source_buffer.source
|
176
212
|
else
|
177
213
|
ast, comments = parse( source, options[:file] )
|
178
|
-
comments = Parser::Source::Comment.associate(ast, comments)
|
214
|
+
comments = ast ? Parser::Source::Comment.associate(ast, comments) : {}
|
179
215
|
end
|
180
216
|
|
217
|
+
namespace = Namespace.new
|
218
|
+
|
181
219
|
filters = (options[:filters] || Filter::DEFAULTS)
|
182
220
|
|
183
221
|
unless filters.empty?
|
@@ -192,7 +230,14 @@ module Ruby2JS
|
|
192
230
|
filter = filter.new(comments)
|
193
231
|
|
194
232
|
filter.options = options
|
233
|
+
filter.namespace = namespace
|
195
234
|
ast = filter.process(ast)
|
235
|
+
|
236
|
+
unless filter.prepend_list.empty?
|
237
|
+
prepend = filter.prepend_list.sort_by {|ast| ast.type == :import ? 0 : 1}
|
238
|
+
prepend.reject! {|ast| ast.type == :import} if filter.disable_autoimports
|
239
|
+
ast = Parser::AST::Node.new(:begin, [*prepend, ast])
|
240
|
+
end
|
196
241
|
end
|
197
242
|
|
198
243
|
ruby2js = Ruby2JS::Converter.new(ast, comments)
|
@@ -203,7 +248,11 @@ module Ruby2JS
|
|
203
248
|
ruby2js.strict = options[:strict]
|
204
249
|
ruby2js.comparison = options[:comparison] || :equality
|
205
250
|
ruby2js.or = options[:or] || :logical
|
206
|
-
ruby2js.
|
251
|
+
ruby2js.module_type = options[:module] || :esm
|
252
|
+
ruby2js.underscored_private = (options[:eslevel] < 2022) || options[:underscored_private]
|
253
|
+
|
254
|
+
ruby2js.namespace = namespace
|
255
|
+
|
207
256
|
if ruby2js.binding and not ruby2js.ivars
|
208
257
|
ruby2js.ivars = ruby2js.binding.eval \
|
209
258
|
'Hash[instance_variables.map {|var| [var, instance_variable_get(var)]}]'
|
@@ -221,6 +270,8 @@ module Ruby2JS
|
|
221
270
|
|
222
271
|
ruby2js.timestamp options[:file]
|
223
272
|
|
273
|
+
ruby2js.file_name = options[:file] || ast&.loc&.expression&.source_buffer&.name || ''
|
274
|
+
|
224
275
|
ruby2js
|
225
276
|
end
|
226
277
|
|
@@ -228,16 +279,16 @@ module Ruby2JS
|
|
228
279
|
buffer = Parser::Source::Buffer.new(file, line)
|
229
280
|
buffer.source = source.encode('utf-8')
|
230
281
|
parser = Parser::CurrentRuby.new
|
231
|
-
parser.
|
232
|
-
|
233
|
-
|
234
|
-
|
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)
|
235
286
|
rescue Parser::SyntaxError => e
|
236
287
|
split = source[0..e.diagnostic.location.begin_pos].split("\n")
|
237
288
|
line, col = split.length, split.last.length
|
238
289
|
message = "line #{line}, column #{col}: #{e.diagnostic.message}"
|
239
290
|
message += "\n in file #{file}" if file
|
240
|
-
raise Ruby2JS::SyntaxError.new(message)
|
291
|
+
raise Ruby2JS::SyntaxError.new(message, e.diagnostic)
|
241
292
|
end
|
242
293
|
|
243
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()
|
@@ -130,7 +130,7 @@ module Ruby2JS
|
|
130
130
|
Parser::AST::Node.new(type, args)
|
131
131
|
end
|
132
132
|
|
133
|
-
attr_accessor :strict, :eslevel, :comparison, :or, :underscored_private
|
133
|
+
attr_accessor :strict, :eslevel, :module_type, :comparison, :or, :underscored_private
|
134
134
|
|
135
135
|
def es2015
|
136
136
|
@eslevel >= 2015
|
@@ -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|
|
@@ -248,7 +252,7 @@ module Ruby2JS
|
|
248
252
|
if ast.loc and ast.loc.expression
|
249
253
|
filename = ast.loc.expression.source_buffer.name
|
250
254
|
if filename and not filename.empty?
|
251
|
-
@timestamps[filename] ||= File.mtime(filename)
|
255
|
+
@timestamps[filename] ||= File.mtime(filename) rescue nil
|
252
256
|
end
|
253
257
|
end
|
254
258
|
|
@@ -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
|
|