ruby2js 3.5.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 +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
|
|