ruby2js 3.1.1 → 3.3.2

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +52 -11
  3. data/lib/ruby2js.rb +14 -2
  4. data/lib/ruby2js/converter.rb +11 -3
  5. data/lib/ruby2js/converter/args.rb +1 -1
  6. data/lib/ruby2js/converter/block.rb +2 -2
  7. data/lib/ruby2js/converter/class.rb +4 -3
  8. data/lib/ruby2js/converter/class2.rb +38 -9
  9. data/lib/ruby2js/converter/cvar.rb +1 -1
  10. data/lib/ruby2js/converter/cvasgn.rb +1 -1
  11. data/lib/ruby2js/converter/def.rb +2 -2
  12. data/lib/ruby2js/converter/for.rb +7 -0
  13. data/lib/ruby2js/converter/hash.rb +3 -3
  14. data/lib/ruby2js/converter/if.rb +13 -2
  15. data/lib/ruby2js/converter/import.rb +38 -0
  16. data/lib/ruby2js/converter/logical.rb +46 -1
  17. data/lib/ruby2js/converter/opasgn.rb +5 -2
  18. data/lib/ruby2js/converter/regexp.rb +27 -2
  19. data/lib/ruby2js/converter/return.rb +1 -1
  20. data/lib/ruby2js/converter/send.rb +53 -27
  21. data/lib/ruby2js/converter/super.rb +15 -9
  22. data/lib/ruby2js/converter/xnode.rb +89 -0
  23. data/lib/ruby2js/es2021.rb +5 -0
  24. data/lib/ruby2js/es2021/strict.rb +3 -0
  25. data/lib/ruby2js/filter/cjs.rb +2 -2
  26. data/lib/ruby2js/filter/esm.rb +72 -0
  27. data/lib/ruby2js/filter/functions.rb +191 -41
  28. data/lib/ruby2js/filter/matchAll.rb +49 -0
  29. data/lib/ruby2js/filter/node.rb +18 -10
  30. data/lib/ruby2js/filter/nokogiri.rb +13 -13
  31. data/lib/ruby2js/filter/react.rb +190 -30
  32. data/lib/ruby2js/filter/require.rb +1 -4
  33. data/lib/ruby2js/filter/rubyjs.rb +4 -4
  34. data/lib/ruby2js/filter/vue.rb +40 -15
  35. data/lib/ruby2js/filter/wunderbar.rb +63 -0
  36. data/lib/ruby2js/serializer.rb +25 -11
  37. data/lib/ruby2js/version.rb +2 -2
  38. data/ruby2js.gemspec +2 -11
  39. metadata +24 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c818d7a2fb520e13fea6b323e1c0b0fc1feb696d4fc4acdd2eff72a5de29ca22
4
- data.tar.gz: 161e0c26b4f648c85522dc6f54d5802b3feae071427790f20df9354ef742a49c
3
+ metadata.gz: a7be3ac378426bc010a8e435f3be72cf21e2b0097f75e05c320e972df2d40de2
4
+ data.tar.gz: 615394a93302b09a241569996ba0186eda723fafe751ceca62dc4521d0bf3b56
5
5
  SHA512:
6
- metadata.gz: 575f68cebfd725c1d43b3eb0ee2aea8174b867b42839b5c1ec237999fdf68a2be6251a4130fb6a4a438597c55e874c41448ebb745eb60e311713e49c9bc4cb0c
7
- data.tar.gz: a8ea6eb2cdbae3a2ac24cad9101a6496a7b2f3d7865cbda1535bfb6d034694cd618d5c3b843db49267e29cb87f7acbc1bd6552da17bfb4115a0a4f6f31151050
6
+ metadata.gz: 6309c00069f51a2b78f14f4fb2c7d45364449b82f74a1fc91af420bea17f5b4d112b11963a92873baa1f72b049e4d1060221f2fd59b762f3af6a780063be2eb2
7
+ data.tar.gz: ecbed895c7319cbce8e8faafc510a0ab64e4a0e3eb14ed9a2d31ebbef4bcd131d52e8340dbc95d9c6ecdff5dc21c919722e76d74f8f940802b3d55484b1083ae
data/README.md CHANGED
@@ -92,6 +92,18 @@ Enable strict support:
92
92
  puts Ruby2JS.convert('a=1', strict: true)
93
93
  ```
94
94
 
95
+ Emit strict equality comparisons:
96
+
97
+ ```ruby
98
+ puts Ruby2JS.convert('a==1', comparison: :identity)
99
+ ```
100
+
101
+ Emit nullish coalescing operators:
102
+
103
+ ```ruby
104
+ puts Ruby2JS.convert('a || 1', or: :nullish)
105
+ ```
106
+
95
107
  With [ExecJS](https://github.com/sstephenson/execjs):
96
108
  ```ruby
97
109
  require 'ruby2js/execjs'
@@ -209,7 +221,7 @@ the script.
209
221
 
210
222
  * <a id="camelCase" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/camelCase.rb">camelCase</a>
211
223
  converts `underscore_case` to `camelCase`. See
212
- [camelCase_spec](https://github.com/rubys/ruby2js/blob/master/spec/camelCase_spec.rb)
224
+ [camelcase_spec](https://github.com/rubys/ruby2js/blob/master/spec/camelcase_spec.rb)
213
225
  for examples.
214
226
 
215
227
  * <a id="functions" href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter/functions.rb">functions</a>
@@ -220,9 +232,10 @@ the script.
220
232
  * `.clear` becomes `.length = 0`
221
233
  * `.delete` becomes `delete target[arg]`
222
234
  * `.downcase` becomes `.toLowerCase`
223
- * `.each` becomes `for (i in ...) {}`
224
- * `.each_key` becomes `Object.keys().forEach`
225
- * `.each_value` becomes `for (i in ...) {}`
235
+ * `.each` becomes `.forEach`
236
+ * `.each_key` becomes `for (i in ...) {}`
237
+ * `.each_pair` becomes `for (var key in item) {var value = item[key]; ...}`
238
+ * `.each_value` becomes `.forEach`
226
239
  * `.each_with_index` becomes `.forEach`
227
240
  * `.end_with?` becomes `.slice(-arg.length) == arg`
228
241
  * `.empty?` becomes `.length == 0`
@@ -232,9 +245,10 @@ the script.
232
245
  * `.gsub` becomes `replace(//g)`
233
246
  * `.include?` becomes `.indexOf() != -1`
234
247
  * `.inspect` becomes `JSON.stringify()`
235
- * `.keys` becomes `Object.keys()`
248
+ * `.keys()` becomes `Object.keys()`
236
249
  * `.last` becomes `[*.length-1]`
237
250
  * `.last(n)` becomes `.slice(*.length-1, *.length)`
251
+ * `.lstrip` becomes `.replace(/^\s+/, "")`
238
252
  * `.max` becomes `Math.max.apply(Math)`
239
253
  * `.merge` becomes `Object.assign({}, ...)`
240
254
  * `.merge!` becomes `Object.assign()`
@@ -244,6 +258,7 @@ the script.
244
258
  * `puts` becomes `console.log`
245
259
  * `.replace` becomes `.length = 0; ...push.apply(*)`
246
260
  * `.respond_to?` becomes `right in left`
261
+ * `.rstrip` becomes `.replace(/s+$/, "")`
247
262
  * `.scan` becomes `.match(//g)`
248
263
  * `.start_with?` becomes `.substring(0, arg.length) == arg`
249
264
  * `.upto(lim)` becomes `for (var i=num; i<=lim; i+=1)`
@@ -263,6 +278,7 @@ the script.
263
278
  * `[n...m]` becomes `.slice(n,m)`
264
279
  * `[n..m]` becomes `.slice(n,m+1)`
265
280
  * `[/r/, n]` becomes `.match(/r/)[n]`
281
+ * `[/r/, n]=` becomes `.replace(/r/, ...)`
266
282
  * `(1..2).each {|i| ...}` becomes `for (var i=1 i<=2; i+=1)`
267
283
  * `"string" * length` becomes `new Array(length + 1).join("string")`
268
284
  * `.sub!` and `.gsub!` become equivalent `x = x.replace` statements
@@ -280,6 +296,7 @@ the script.
280
296
  * New classes subclassed off of `Exception` will become subclassed off
281
297
  of `Error` instead; and default constructors will be provided
282
298
  * `loop do...end` will be replaced with `while (true) {...}`
299
+ * `raise Exception.new(...)` will be replaced with `throw new Error(...)`
283
300
 
284
301
  Additionally, there is one mapping that will only be done if explicitly
285
302
  <a href="https://github.com/rubys/ruby2js/blob/master/lib/ruby2js/filter.rb">included</a>:
@@ -515,6 +532,16 @@ the script.
515
532
  * maps `export default async proc` to `module.exports = async`
516
533
  * maps `export default` to `module.exports =`
517
534
 
535
+ * <a id="matchAll" href="https://github.com/rubys/ruby2js/blob/master/spec/matchAll">matchAll</a>
536
+
537
+ For ES level < 2020:
538
+
539
+ * maps `str.matchAll(pattern).forEach {}` to
540
+ `while (match = pattern.exec(str)) {}`
541
+
542
+ Note `pattern` must be a simple variable with a value of a regular
543
+ expression with the `g` flag set at runtime.
544
+
518
545
  [Wunderbar](https://github.com/rubys/wunderbar) includes additional demos:
519
546
 
520
547
  * [chat](https://github.com/rubys/wunderbar/blob/master/demo/chat.rb),
@@ -536,7 +563,7 @@ conversions are made:
536
563
  * `def f(a, (foo, *bar))` becomes `function f(a, [foo, ...bar])`
537
564
  * `def a(b=1)` becomes `function a(b=1)`
538
565
  * `def a(*b)` becomes `function a(...b)`
539
- * `.each_key` becomes `for (i of ...) {}`
566
+ * `.each_value` becomes `for (i of ...) {}`
540
567
  * `a(*b)` becomes `a(...b)`
541
568
  * `"#{a}"` becomes <code>\`${a}\`</code>
542
569
  * `lambda {|x| x}` becomes `(x) => {return x}`
@@ -572,9 +599,11 @@ ES2017 support
572
599
  ---
573
600
 
574
601
  When option `eslevel: 2017` is provided, the following additional
575
- conversion is made by the `functions` filter:
602
+ conversions are made by the `functions` filter:
576
603
 
577
- * `.each_entry` becomes `Object.entries().forEach`
604
+ * `.values()` becomes `Object.values()`
605
+ * `.entries()` becomes `Object.entries()`
606
+ * `.each_pair {}` becomes `for (let [key, value] of Object.entries()) {}'
578
607
 
579
608
  ES2018 support
580
609
  ---
@@ -590,13 +619,14 @@ optional keyword arguments.
590
619
  ES2019 support
591
620
  ---
592
621
 
593
- When option `eslevel: 2018` is provided, the following additional
622
+ When option `eslevel: 2019` is provided, the following additional
594
623
  conversion is made by the `functions` filter:
595
624
 
596
625
  * `.flatten` becomes `.flat(Infinity)`
597
626
  * `.lstrip` becomes `.trimEnd
598
627
  * `.rstrip` becomes `.trimStart
599
628
  * `a.to_h` becomes `Object.fromEntries(a)`
629
+ * `Hash[a]` becomes `Object.fromEntries(a)`
600
630
 
601
631
  Additionally, `rescue` without a variable will map to `catch` without a
602
632
  variable.
@@ -604,11 +634,22 @@ variable.
604
634
  ES2020 support
605
635
  ---
606
636
 
607
- When option `eslevel: 2018` is provided, the following additional
608
- conversion is made:
637
+ When option `eslevel: 2020` is provided, the following additional
638
+ conversions are made:
609
639
 
610
640
  * `@x` becomes `this.#x`
611
641
  * `@@x` becomes `ClassName.#x`
642
+ * `a&.b` becomes `a?.b`
643
+ * `.scan` becomes `Array.from(str.matchAll(/.../g), s => s.slice(1))`
644
+
645
+ ES2021 support
646
+ ---
647
+
648
+ When option `eslevel: 2021` is provided, the following additional
649
+ conversions are made:
650
+
651
+ * `x ||= 1` becomes `x ||= 1`
652
+ * `x &&= 1` becomes `x &&= 1`
612
653
 
613
654
  Picking a Ruby to JS mapping tool
614
655
  ---
@@ -94,6 +94,10 @@ module Ruby2JS
94
94
  @options[:eslevel] >= 2020
95
95
  end
96
96
 
97
+ def es2021
98
+ @options[:eslevel] >= 2021
99
+ end
100
+
97
101
  def process(node)
98
102
  ast, @ast = @ast, node
99
103
  replacement = super
@@ -127,6 +131,9 @@ module Ruby2JS
127
131
  def on_sendw(node); on_send(node); end
128
132
  def on_undefined?(node); on_defined?(node); end
129
133
  def on_nil(node); end
134
+ def on_xnode(node); end
135
+ def on_export(node); end
136
+ def on_import(node); end
130
137
 
131
138
  # provide a method so filters can call 'super'
132
139
  def on_sym(node); node; end
@@ -156,7 +163,7 @@ module Ruby2JS
156
163
 
157
164
  if Proc === source
158
165
  file,line = source.source_location
159
- source = File.read(file.dup.untaint).untaint
166
+ source = IO.read(file)
160
167
  ast, comments = parse(source)
161
168
  comments = Parser::Source::Comment.associate(ast, comments) if ast
162
169
  ast = find_block( ast, line )
@@ -169,9 +176,12 @@ module Ruby2JS
169
176
  comments = Parser::Source::Comment.associate(ast, comments) if ast
170
177
  end
171
178
 
172
- filters = options[:filters] || Filter::DEFAULTS
179
+ filters = (options[:filters] || Filter::DEFAULTS)
173
180
 
174
181
  unless filters.empty?
182
+ filters.dup.each do |filter|
183
+ filters = filter.reorder(filters) if filter.respond_to? :reorder
184
+ end
175
185
 
176
186
  filter = Filter::Processor
177
187
  filters.reverse.each do |mod|
@@ -189,6 +199,8 @@ module Ruby2JS
189
199
  ruby2js.ivars = options[:ivars]
190
200
  ruby2js.eslevel = options[:eslevel]
191
201
  ruby2js.strict = options[:strict]
202
+ ruby2js.comparison = options[:comparison] || :equality
203
+ ruby2js.or = options[:or] || :logical
192
204
  if ruby2js.binding and not ruby2js.ivars
193
205
  ruby2js.ivars = ruby2js.binding.eval \
194
206
  'Hash[instance_variables.map {|var| [var, instance_variable_get(var)]}]'
@@ -28,7 +28,7 @@ module Ruby2JS
28
28
  :=== => :'!=='
29
29
  }
30
30
 
31
- GROUP_OPERATORS = [:begin, :dstr, :dsym, :and, :or, :casgn]
31
+ GROUP_OPERATORS = [:begin, :dstr, :dsym, :and, :or, :casgn, :if]
32
32
 
33
33
  VASGN = [:cvasgn, :ivasgn, :gvasgn, :lvasgn]
34
34
 
@@ -57,9 +57,12 @@ module Ruby2JS
57
57
  @prototype = nil
58
58
  @class_parent = nil
59
59
  @class_name = nil
60
+ @jsx = false
60
61
 
61
62
  @eslevel = :es5
62
63
  @strict = false
64
+ @comparison = :equality
65
+ @or = :logical
63
66
  end
64
67
 
65
68
  def width=(width)
@@ -124,7 +127,7 @@ module Ruby2JS
124
127
  Parser::AST::Node.new(type, args)
125
128
  end
126
129
 
127
- attr_accessor :strict, :eslevel
130
+ attr_accessor :strict, :eslevel, :comparison, :or
128
131
 
129
132
  def es2015
130
133
  @eslevel >= 2015
@@ -150,6 +153,10 @@ module Ruby2JS
150
153
  @eslevel >= 2020
151
154
  end
152
155
 
156
+ def es2021
157
+ @eslevel >= 2021
158
+ end
159
+
153
160
  @@handlers = []
154
161
  def self.handle(*types, &block)
155
162
  types.each do |type|
@@ -238,7 +245,6 @@ module Ruby2JS
238
245
  if ast.loc and ast.loc.expression
239
246
  filename = ast.loc.expression.source_buffer.name
240
247
  if filename and not filename.empty?
241
- filename = filename.dup.untaint
242
248
  @timestamps[filename] ||= File.mtime(filename)
243
249
  end
244
250
  end
@@ -307,6 +313,7 @@ require 'ruby2js/converter/for'
307
313
  require 'ruby2js/converter/hash'
308
314
  require 'ruby2js/converter/if'
309
315
  require 'ruby2js/converter/in'
316
+ require 'ruby2js/converter/import'
310
317
  require 'ruby2js/converter/ivar'
311
318
  require 'ruby2js/converter/ivasgn'
312
319
  require 'ruby2js/converter/kwbegin'
@@ -333,3 +340,4 @@ require 'ruby2js/converter/vasgn'
333
340
  require 'ruby2js/converter/while'
334
341
  require 'ruby2js/converter/whilepost'
335
342
  require 'ruby2js/converter/xstr'
343
+ require 'ruby2js/converter/xnode'
@@ -41,7 +41,7 @@ module Ruby2JS
41
41
  end
42
42
 
43
43
  handle :mlhs do |*args|
44
- if es2015
44
+ if es2015 or @jsx
45
45
  put '['
46
46
  parse_all(*args, join: ', ')
47
47
  put ']'
@@ -9,7 +9,7 @@ module Ruby2JS
9
9
 
10
10
  handle :block do |call, args, block|
11
11
 
12
- if
12
+ if \
13
13
  @state == :statement and args.children.length == 1 and
14
14
  call.children.first and call.children.first.type == :begin and
15
15
  call.children[1] == :step and
@@ -42,7 +42,7 @@ module Ruby2JS
42
42
  @vars = vars if es2015
43
43
  end
44
44
 
45
- elsif
45
+ elsif \
46
46
  call.children[0] == nil and call.children[1] == :function and
47
47
  call.children[2..-1].all? do |child|
48
48
  child.type == :lvar or (child.type == :send and
@@ -37,7 +37,7 @@ module Ruby2JS
37
37
  body.compact!
38
38
  visible = {}
39
39
  body.map! do |m|
40
- if
40
+ if \
41
41
  @ast.type == :class_module and m.type == :defs and
42
42
  m.children.first == s(:self)
43
43
  then
@@ -196,7 +196,8 @@ module Ruby2JS
196
196
 
197
197
  if inheritance
198
198
  body.unshift s(:send, name, :prototype=,
199
- s(:send, s(:const, nil, :Object), :create, inheritance)),
199
+ s(:send, s(:const, nil, :Object), :create,
200
+ s(:attr, inheritance, :prototype))),
200
201
  s(:send, s(:attr, name, :prototype), :constructor=, name)
201
202
  else
202
203
  body.compact!
@@ -217,7 +218,7 @@ module Ruby2JS
217
218
  end
218
219
 
219
220
  # collapse sequence to a single assignment
220
- if
221
+ if \
221
222
  @ast.type != :class_extend and
222
223
  (methods > 1 or (methods == 1 and body[start].type == :prop))
223
224
  then
@@ -56,10 +56,30 @@ module Ruby2JS
56
56
  # find ivars and cvars
57
57
  walk = proc do |ast|
58
58
  ivars << ast.children.first if ast.type === :ivar
59
+ ivars << ast.children.first if ast.type === :ivasgn
59
60
  cvars << ast.children.first if ast.type === :cvar
61
+ cvars << ast.children.first if ast.type === :cvasgn
62
+
60
63
  ast.children.each do |child|
61
64
  walk[child] if child.is_a? Parser::AST::Node
62
65
  end
66
+
67
+ if ast.type == :send and ast.children.first == nil
68
+ if ast.children[1] == :attr_accessor
69
+ ast.children[2..-1].each_with_index do |child_sym, index2|
70
+ ivars << :"@#{child_sym.children.first}"
71
+ end
72
+ elsif ast.children[1] == :attr_reader
73
+ ast.children[2..-1].each_with_index do |child_sym, index2|
74
+ ivars << :"@#{child_sym.children.first}"
75
+ end
76
+ elsif ast.children[1] == :attr_writer
77
+ ast.children[2..-1].each_with_index do |child_sym, index2|
78
+ ivars << :"@#{child_sym.children.first}"
79
+ end
80
+ end
81
+ end
82
+
63
83
  end
64
84
  walk[@ast]
65
85
 
@@ -77,7 +97,7 @@ module Ruby2JS
77
97
  cvars.to_a.sort.each do |cvar|
78
98
  put(index == 0 ? @nl : @sep)
79
99
  index += 1
80
- put 'static #' + cvar.to_s[2..-1]
100
+ put 'static #$' + cvar.to_s[2..-1]
81
101
  end
82
102
 
83
103
  while constructor.length > 0 and constructor.first.type == :ivasgn
@@ -131,7 +151,7 @@ module Ruby2JS
131
151
  next
132
152
  end
133
153
 
134
- m = m.updated(m.type, [@prop, m.children[1], *constructor])
154
+ m = m.updated(m.type, [@prop, m.children[1], s(:begin, *constructor)])
135
155
  elsif not m.is_method?
136
156
  @prop = "get #{@prop}"
137
157
  m = m.updated(m.type, [*m.children[0..1],
@@ -147,12 +167,13 @@ module Ruby2JS
147
167
 
148
168
  begin
149
169
  @instance_method = m
170
+ @class_method = nil
150
171
  parse m # unless skipped
151
172
  ensure
152
173
  @instance_method = nil
153
174
  end
154
175
 
155
- elsif
176
+ elsif \
156
177
  [:defs, :asyncs].include? m.type and m.children.first.type == :self
157
178
  then
158
179
 
@@ -172,27 +193,35 @@ module Ruby2JS
172
193
  @prop.sub! 'static', 'static async' if m.type == :asyncs
173
194
 
174
195
  m = m.updated(:def, m.children[1..3])
175
- parse m
196
+ begin
197
+ @instance_method = nil
198
+ @class_method = m
199
+ parse m # unless skipped
200
+ ensure
201
+ @instance_method = nil
202
+ end
176
203
 
177
204
  elsif m.type == :send and m.children.first == nil
205
+ p = es2020 ? '#' : '_'
206
+
178
207
  if m.children[1] == :attr_accessor
179
208
  m.children[2..-1].each_with_index do |child_sym, index2|
180
209
  put @sep unless index2 == 0
181
210
  var = child_sym.children.first
182
- put "get #{var}() {#{@nl}return this._#{var}#@nl}#@sep"
183
- put "set #{var}(#{var}) {#{@nl}this._#{var} = #{var}#@nl}"
211
+ put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}#@sep"
212
+ put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
184
213
  end
185
214
  elsif m.children[1] == :attr_reader
186
215
  m.children[2..-1].each_with_index do |child_sym, index2|
187
216
  put @sep unless index2 == 0
188
217
  var = child_sym.children.first
189
- put "get #{var}() {#{@nl}return this._#{var}#@nl}"
218
+ put "get #{var}() {#{@nl}return this.#{p}#{var}#@nl}"
190
219
  end
191
220
  elsif m.children[1] == :attr_writer
192
221
  m.children[2..-1].each_with_index do |child_sym, index2|
193
222
  put @sep unless index2 == 0
194
223
  var = child_sym.children.first
195
- put "set #{var}(#{var}) {#{@nl}this._#{var} = #{var}#@nl}"
224
+ put "set #{var}(#{var}) {#{@nl}this.#{p}#{var} = #{var}#@nl}"
196
225
  end
197
226
  elsif [:private, :protected, :public].include? m.children[1]
198
227
  raise Error.new("class #{m.children[1]} is not supported", @ast)
@@ -208,7 +237,7 @@ module Ruby2JS
208
237
 
209
238
  else
210
239
  if m.type == :cvasgn and es2020
211
- put 'static #'; put m.children[0].to_s[2..-1]; put ' = '
240
+ put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
212
241
  parse m.children[1]
213
242
  else
214
243
  skipped = true
@@ -4,7 +4,7 @@ module Ruby2JS
4
4
  # (cvar :@@a)
5
5
 
6
6
  handle :cvar do |var|
7
- prefix = es2020 ? '#' : '_'
7
+ prefix = es2020 ? '#$' : '_'
8
8
 
9
9
  @class_name ||= nil
10
10
  if @class_name