ruby2js 3.0.15 → 3.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7acab4a28c4ec163da40b2f1847692df089ff593af8d735bf163cfe566ed7f5b
4
- data.tar.gz: f0f33e2c6beba7785d535ece64e4402cb5aac29fca9f9541087857905ba33d8f
3
+ metadata.gz: f0d87e8dda198550df6b79aa5a14e033ff6180f6057998d63252a41011d04735
4
+ data.tar.gz: 1869aa7759c6b40137258b3a87c0c250944940753cc5d0814a0e027bd88068dd
5
5
  SHA512:
6
- metadata.gz: d53db4145f632be93c234cf706b25e6c7c27a0d0c1a864543045e6d9a9672e9e9792281677da6a4b107a3eac0d173ca2cd151a0d0f91acf3171ecc5a84dd06c4
7
- data.tar.gz: 49364f4bb8cf3f05ea9d07664d800a86af81817b096a40c89ec8848a6f45fe62b755cc2e0c995ce49f300bb84a6caa1de4b5fd822b8ae403579e9244dde9e442
6
+ metadata.gz: 75b7232fbc65271e6b200f6283f04605de370d6e38b127e6c51a481931a41076e055a3141fe643094c67e9b73988b0e7ccf576d0d6c36e3f44cbdb56face4362
7
+ data.tar.gz: c60b7631d17f12cafe859cfc06e7551f3158f1d6f4643f2fd2faf12a4cbd85cb8496c453d8c43d6244e73516726251f438f964a2312fd87deb8c690dc098b208
data/README.md CHANGED
@@ -236,6 +236,7 @@ the script.
236
236
  * `.last` becomes `[*.length-1]`
237
237
  * `.last(n)` becomes `.slice(*.length-1, *.length)`
238
238
  * `.max` becomes `Math.max.apply(Math)`
239
+ * `.merge` becomes `Object.assign({}, ...)`
239
240
  * `.merge!` becomes `Object.assign()`
240
241
  * `.min` becomes `Math.min.apply(Math)`
241
242
  * `.nil?` becomes `== null`
@@ -249,6 +250,9 @@ the script.
249
250
  * `.downto(lim)` becomes `for (var i=num; i>=lim; i-=1)`
250
251
  * `.step(lim, n).each` becomes `for (var i=num; i<=lim; i+=n)`
251
252
  * `.step(lim, -n).each` becomes `for (var i=num; i>=lim; i-=n)`
253
+ * `(0..a).to_a` becomes `Array.apply(null, {length: a}).map(Function.call, Number)`
254
+ * `(b..a).to_a` becomes `Array.apply(null, {length: (a-b+1)}).map(Function.call, Number).map(function (idx) { return idx+b })`
255
+ * `(b...a).to_a` becomes `Array.apply(null, {length: (a-b)}).map(Function.call, Number).map(function (idx) { return idx+b })`
252
256
  * `.strip` becomes `.trim`
253
257
  * `.sub` becomes `.replace`
254
258
  * `.to_f` becomes `parseFloat`
@@ -282,7 +286,7 @@ the script.
282
286
 
283
287
  * `.class` becomes `.constructor`
284
288
 
285
- * <a id="node" * href="https://github.com/rubys/ruby2js/blob/master/spec/node_spec.rb">node</a>
289
+ * <a id="node" href="https://github.com/rubys/ruby2js/blob/master/spec/node_spec.rb">node</a>
286
290
 
287
291
  * `` `command` `` becomes `child_process.execSync("command", {encoding: "utf8"})`
288
292
  * `ARGV` becomes `process.argv.slice(2)`
@@ -539,6 +543,9 @@ conversions are made:
539
543
  * `proc {|x| x}` becomes `(x) => {x}`
540
544
  * `a {|x|}` becomes `a((x) => {})`
541
545
  * `class Person; end` becomes `class Person {}`
546
+ * `(0...a).to_a` becomes `[...Array(a).keys()]`
547
+ * `(0..a).to_a` becomes `[...Array(a+1).keys()]`
548
+ * `(b..a).to_a` becomes `Array.from({length: (a-b+1)}, (_, idx) => idx+b)`
542
549
 
543
550
  ES2015 class support includes constructors, super, methods, class methods,
544
551
  instance methods, instance variables, class variables, getters, setters,
@@ -549,6 +556,9 @@ Additionally, the `functions` filter will provide the following conversion:
549
556
  * `Array(x)` becomes `Array.from(x)`
550
557
  * `.inject(n) {}` becomes `.reduce(() => {}, n)`
551
558
 
559
+ Finally, keyword arguments and optional keyword arguments will be mapped to
560
+ parameter detructuring.
561
+
552
562
  ES2016 support
553
563
  ---
554
564
 
@@ -566,6 +576,40 @@ conversion is made by the `functions` filter:
566
576
 
567
577
  * `.each_entry` becomes `Object.entries().forEach`
568
578
 
579
+ ES2018 support
580
+ ---
581
+
582
+ When option `eslevel: 2018` is provided, the following additional
583
+ conversion is made by the `functions` filter:
584
+
585
+ * `.merge` becomes `{...a, ...b}`
586
+
587
+ Additionally, rest arguments can now be used with keyword arguments and
588
+ optional keyword arguments.
589
+
590
+ ES2019 support
591
+ ---
592
+
593
+ When option `eslevel: 2018` is provided, the following additional
594
+ conversion is made by the `functions` filter:
595
+
596
+ * `.flatten` becomes `.flat(Infinity)`
597
+ * `.lstrip` becomes `.trimEnd
598
+ * `.rstrip` becomes `.trimStart
599
+ * `a.to_h` becomes `Object.fromEntries(a)`
600
+
601
+ Additionally, `rescue` without a variable will map to `catch` without a
602
+ variable.
603
+
604
+ ES2020 support
605
+ ---
606
+
607
+ When option `eslevel: 2018` is provided, the following additional
608
+ conversion is made:
609
+
610
+ * `@x` becomes `this.#x`
611
+ * `@@x` becomes `ClassName.#x`
612
+
569
613
  Picking a Ruby to JS mapping tool
570
614
  ---
571
615
 
data/lib/ruby2js.rb CHANGED
@@ -81,6 +81,18 @@ module Ruby2JS
81
81
  @options[:eslevel] >= 2017
82
82
  end
83
83
 
84
+ def es2018
85
+ @options[:eslevel] >= 2018
86
+ end
87
+
88
+ def es2019
89
+ @options[:eslevel] >= 2019
90
+ end
91
+
92
+ def es2020
93
+ @options[:eslevel] >= 2020
94
+ end
95
+
84
96
  def process(node)
85
97
  ast, @ast = @ast, node
86
98
  replacement = super
@@ -201,7 +213,9 @@ module Ruby2JS
201
213
  buffer.source = source.encode('utf-8')
202
214
  parser = Parser::CurrentRuby.new
203
215
  parser.builder.emit_file_line_as_literals=false
204
- parser.parse_with_comments(buffer)
216
+ ast, comments = parser.parse_with_comments(buffer)
217
+ Parser::CurrentRuby.parse(source.encode('utf-8')) unless ast
218
+ [ast, comments]
205
219
  rescue Parser::SyntaxError => e
206
220
  split = source[0..e.diagnostic.location.begin_pos].split("\n")
207
221
  line, col = split.length, split.last.length
@@ -138,6 +138,18 @@ module Ruby2JS
138
138
  @eslevel >= 2017
139
139
  end
140
140
 
141
+ def es2018
142
+ @eslevel >= 2018
143
+ end
144
+
145
+ def es2019
146
+ @eslevel >= 2019
147
+ end
148
+
149
+ def es2020
150
+ @eslevel >= 2020
151
+ end
152
+
141
153
  @@handlers = []
142
154
  def self.handle(*types, &block)
143
155
  types.each do |type|
@@ -7,7 +7,37 @@ module Ruby2JS
7
7
  # (blockarg :c))
8
8
 
9
9
  handle :args do |*args|
10
+ kwargs = []
11
+ while args.last and
12
+ [:kwarg, :kwoptarg, :kwrestarg].include? args.last.type
13
+ kwargs.unshift args.pop
14
+ end
15
+
16
+ if kwargs.length == 1 and kwargs.last.type == :kwrestarg
17
+ args.push s(:arg, *kwargs.last.children)
18
+ end
19
+
20
+ unless kwargs.empty? or es2015
21
+ raise NotImplementedError.new('Keyword args require ES2015')
22
+ end
23
+
10
24
  parse_all(*args, join: ', ')
25
+ if not kwargs.empty?
26
+ put ', ' unless args.empty?
27
+ put '{ '
28
+ kwargs.each_with_index do |kw, index|
29
+ put ', ' unless index == 0
30
+ if kw.type == :kwarg
31
+ put kw.children.first
32
+ elsif kw.type == :kwoptarg
33
+ put kw.children.first; put ' = '; parse kw.children.last
34
+ elsif kw.type == :kwrestarg
35
+ raise 'Rest arg requires ES2018' unless es2018
36
+ put '...'; put kw.children.first
37
+ end
38
+ end
39
+ put ' }'
40
+ end
11
41
  end
12
42
 
13
43
  handle :mlhs do |*args|
@@ -8,7 +8,9 @@ module Ruby2JS
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
10
  begin
11
- if @state == :statement
11
+ cbase ||= @rbstack.map {|rb| rb[var]}.compact.last
12
+
13
+ if @state == :statement and not cbase
12
14
  if es2015
13
15
  put "const "
14
16
  else
@@ -16,7 +18,6 @@ module Ruby2JS
16
18
  end
17
19
  end
18
20
 
19
- cbase ||= @rbstack.map {|rb| rb[var]}.compact.last
20
21
  (parse cbase; put '.') if cbase
21
22
 
22
23
  put "#{ var } = "; parse value
@@ -33,21 +33,79 @@ module Ruby2JS
33
33
  class_name, @class_name = @class_name, name
34
34
  class_parent, @class_parent = @class_parent, inheritance
35
35
  @rbstack.push({})
36
+ constructor = []
37
+ index = 0
36
38
 
37
- # capture method names for automatic self referencing
38
- body.each_with_index do |m, index|
39
+ # capture constructor, method names for automatic self referencing
40
+ body.each do |m|
39
41
  if m.type == :def
40
42
  prop = m.children.first
41
- unless prop == :initialize or prop.to_s.end_with? '='
43
+ if prop == :initialize
44
+ constructor = m.children[2..-1]
45
+ elsif not prop.to_s.end_with? '='
42
46
  @rbstack.last[prop] = s(:self)
43
47
  end
44
48
  end
45
49
  end
46
50
 
51
+ # private variable declarations
52
+ if es2020
53
+ ivars = Set.new
54
+ cvars = Set.new
55
+
56
+ # find ivars and cvars
57
+ walk = proc do |ast|
58
+ ivars << ast.children.first if ast.type === :ivar
59
+ cvars << ast.children.first if ast.type === :cvar
60
+ ast.children.each do |child|
61
+ walk[child] if child.is_a? Parser::AST::Node
62
+ end
63
+ end
64
+ walk[@ast]
65
+
66
+ # process leading initializers in constructor
67
+ while constructor.length == 1 and constructor.first.type == :begin
68
+ constructor = constructor.first.children.dup
69
+ end
70
+
71
+ # emit additional class declarations
72
+ unless cvars.empty?
73
+ body.each do |m|
74
+ cvars.delete m.children.first if m.type == :cvasgn
75
+ end
76
+ end
77
+ cvars.to_a.sort.each do |cvar|
78
+ put(index == 0 ? @nl : @sep)
79
+ index += 1
80
+ put 'static #' + cvar.to_s[2..-1]
81
+ end
82
+
83
+ while constructor.length > 0 and constructor.first.type == :ivasgn
84
+ put(index == 0 ? @nl : @sep)
85
+ index += 1
86
+ statement = constructor.shift
87
+ put '#'
88
+ put statement.children.first.to_s[1..-1]
89
+ put ' = '
90
+ parse statement.children.last
91
+
92
+ ivars.delete statement.children.first
93
+ end
94
+
95
+ # emit additional instance declarations
96
+ ivars.to_a.sort.each do |ivar|
97
+ put(index == 0 ? @nl : @sep)
98
+ index += 1
99
+ put '#' + ivar.to_s[1..-1]
100
+ end
101
+ end
102
+
103
+ # process class definition
47
104
  post = []
48
105
  skipped = false
49
- body.each_with_index do |m, index|
106
+ body.each do |m|
50
107
  put(index == 0 ? @nl : @sep) unless skipped
108
+ index += 1
51
109
  comments = comments(m)
52
110
  location = output_location
53
111
  skipped = false
@@ -67,7 +125,13 @@ module Ruby2JS
67
125
 
68
126
  if @prop == :initialize
69
127
  @prop = :constructor
70
- m = m.updated(m.type, [@prop, *m.children[1..2]])
128
+
129
+ if constructor == [] or constructor == [(:super)]
130
+ skipped = true
131
+ next
132
+ end
133
+
134
+ m = m.updated(m.type, [@prop, m.children[1], *constructor])
71
135
  elsif not m.is_method?
72
136
  @prop = "get #{@prop}"
73
137
  m = m.updated(m.type, [*m.children[0..1],
@@ -83,7 +147,7 @@ module Ruby2JS
83
147
 
84
148
  begin
85
149
  @instance_method = m
86
- parse m
150
+ parse m # unless skipped
87
151
  ensure
88
152
  @instance_method = nil
89
153
  end
@@ -143,10 +207,21 @@ module Ruby2JS
143
207
  end
144
208
 
145
209
  else
146
- skipped = true
210
+ if m.type == :cvasgn and es2020
211
+ put 'static #'; put m.children[0].to_s[2..-1]; put ' = '
212
+ parse m.children[1]
213
+ else
214
+ skipped = true
215
+ end
147
216
 
148
217
  if m.type == :casgn and m.children[0] == nil
149
218
  @rbstack.last[m.children[1]] = name
219
+
220
+ if es2020
221
+ put 'static '; put m.children[1].to_s; put ' = '
222
+ parse m.children[2]
223
+ skipped = false
224
+ end
150
225
  elsif m.type == :alias
151
226
  @rbstack.last[m.children[0]] = name
152
227
  end
@@ -4,14 +4,16 @@ module Ruby2JS
4
4
  # (cvar :@@a)
5
5
 
6
6
  handle :cvar do |var|
7
+ prefix = es2020 ? '#' : '_'
8
+
7
9
  @class_name ||= nil
8
10
  if @class_name
9
11
  parse @class_name
10
- put var.to_s.sub('@@', "._")
12
+ put var.to_s.sub('@@', ".#{prefix}")
11
13
  elsif @prototype
12
- put var.to_s.sub('@@', 'this._')
14
+ put var.to_s.sub('@@', "this.#{prefix}")
13
15
  else
14
- put var.to_s.sub('@@', 'this.constructor._')
16
+ put var.to_s.sub('@@', "this.constructor.#{prefix}")
15
17
  end
16
18
  end
17
19
  end
@@ -7,13 +7,15 @@ module Ruby2JS
7
7
  handle :cvasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
+ prefix = es2020 ? '#' : '_'
11
+
10
12
  if @class_name
11
13
  parse @class_name
12
- put var.to_s.sub('@@', "._")
14
+ put var.to_s.sub('@@', ".#{prefix}")
13
15
  elsif @prototype
14
- put var.to_s.sub('@@', 'this._')
16
+ put var.to_s.sub('@@', "this.#{prefix}")
15
17
  else
16
- put var.to_s.sub('@@', 'this.constructor._')
18
+ put var.to_s.sub('@@', "this.constructor.#{prefix}")
17
19
  end
18
20
 
19
21
  if expression
@@ -12,10 +12,26 @@ module Ruby2JS
12
12
 
13
13
  (singleton ? put('{') : puts('{'))
14
14
 
15
- pairs.each_with_index do |node, index|
16
- raise Error.new("kwsplat", @ast) if node.type == :kwsplat
17
-
15
+ index = 0
16
+ while pairs.length > 0
17
+ node = pairs.shift
18
18
  (singleton ? put(', ') : put(",#@ws")) unless index == 0
19
+ index += 1
20
+
21
+ if node.type == :kwsplat
22
+ if es2018
23
+ if node.children.first.type == :hash
24
+ pairs.unshift(*node.children.first.children)
25
+ index = 0
26
+ else
27
+ puts '...'; parse node.children.first
28
+ end
29
+
30
+ next
31
+ else
32
+ raise Error.new("kwsplat", @ast)
33
+ end
34
+ end
19
35
 
20
36
  if not @comments[node].empty?
21
37
  (puts ''; singleton = false) if singleton
@@ -6,6 +6,8 @@ module Ruby2JS
6
6
  handle :ivar do |var|
7
7
  if self.ivars and self.ivars.include? var
8
8
  parse s(:hostvalue, self.ivars[var])
9
+ elsif es2020
10
+ parse s(:attr, s(:self), var.to_s.sub('@', '#'))
9
11
  else
10
12
  parse s(:attr, s(:self), var.to_s.sub('@', '_'))
11
13
  end
@@ -7,7 +7,7 @@ module Ruby2JS
7
7
  handle :ivasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
- put "#{ var.to_s.sub('@', 'this._') }"
10
+ put "#{ var.to_s.sub('@', 'this.' + (es2020 ? '#' : '_')) }"
11
11
  if expression
12
12
  put " = "; parse expression
13
13
  end
@@ -51,13 +51,27 @@ module Ruby2JS
51
51
  puts "try {"; scope body; sput '}'
52
52
 
53
53
  if recovers
54
- var ||= s(:gvar, :$EXCEPTION)
55
54
 
56
55
  if recovers.length == 1 and not recovers.first.children.first
56
+ # find reference to exception ($!)
57
+ walk = proc do |ast|
58
+ result = ast if ast.type === :gvar and ast.children.first == :$!
59
+ ast.children.each do |child|
60
+ result ||= walk[child] if child.is_a? Parser::AST::Node
61
+ end
62
+ result
63
+ end
64
+
57
65
  # single catch with no exception named
58
- put " catch ("; parse var; puts ") {"
66
+ if es2019 and not var and not walk[@ast]
67
+ puts " catch {"
68
+ else
69
+ var ||= s(:gvar, :$EXCEPTION)
70
+ put " catch ("; parse var; puts ") {"
71
+ end
59
72
  scope recovers.first.children.last; sput '}'
60
73
  else
74
+ var ||= s(:gvar, :$EXCEPTION)
61
75
  put " catch ("; parse var; puts ') {'
62
76
 
63
77
  first = true
@@ -16,12 +16,26 @@ module Ruby2JS
16
16
  handle :send, :sendw, :await, :attr, :call do |receiver, method, *args|
17
17
  ast = @ast
18
18
 
19
- if
20
- args.length == 1 and method == :+
21
- then
22
- node = collapse_strings(ast)
23
- return parse node if node != ast
24
- end
19
+ if
20
+ args.length == 1 and method == :+
21
+ then
22
+ node = collapse_strings(ast)
23
+ return parse node if node != ast
24
+ end
25
+
26
+ # :irange support
27
+ # - currently only .to_a
28
+ if
29
+ receiver and
30
+ receiver.type == :begin and
31
+ [:irange, :erange].include? receiver.children.first.type
32
+ then
33
+ unless method == :to_a
34
+ raise Error.new("#{receiver.children.first.type} can only be converted to array currently", receiver.children.first)
35
+ else
36
+ return range_to_array(receiver.children.first)
37
+ end
38
+ end
25
39
 
26
40
  # strip '!' and '?' decorations
27
41
  method = method.to_s[0..-2] if method =~ /\w[!?]$/
@@ -39,7 +53,7 @@ module Ruby2JS
39
53
  end
40
54
 
41
55
  # call anonymous function
42
- if [:call, :[]].include? method and receiver and receiver.type == :block
56
+ if [:call, :[]].include? method and receiver and receiver.type == :block
43
57
  t2,m2,*args2 = receiver.children.first.children
44
58
  if not t2 and [:lambda, :proc].include? m2 and args2.length == 0
45
59
  (es2015 || @state == :statement ? group(receiver) : parse(receiver))
@@ -97,7 +111,7 @@ module Ruby2JS
97
111
 
98
112
  op_index = operator_index method
99
113
  if op_index != -1
100
- target = args.first
114
+ target = args.first
101
115
  end
102
116
 
103
117
  # resolve anonymous receivers against rbstack
@@ -109,7 +123,7 @@ module Ruby2JS
109
123
  group_receiver ||= GROUP_OPERATORS.include? receiver.type
110
124
  group_receiver = false if receiver.children[1] == :[]
111
125
  if receiver.type == :int and !OPERATORS.flatten.include?(method)
112
- group_receiver = true
126
+ group_receiver = true
113
127
  end
114
128
  if not receiver.is_method? and receiver.children.last == :new
115
129
  group_receiver = true
@@ -117,7 +131,7 @@ module Ruby2JS
117
131
  end
118
132
 
119
133
  if target
120
- group_target = target.type == :send &&
134
+ group_target = target.type == :send &&
121
135
  op_index < operator_index( target.children[1] )
122
136
  group_target ||= GROUP_OPERATORS.include? target.type
123
137
  end
@@ -156,7 +170,7 @@ module Ruby2JS
156
170
  put ')'
157
171
 
158
172
  elsif [:-@, :+@, :~, '~'].include? method
159
- if
173
+ if
160
174
  receiver.type == :send and
161
175
  receiver.children[1] == :+@ and
162
176
  Parser::AST::Node === receiver.children[0] and
@@ -235,7 +249,7 @@ module Ruby2JS
235
249
  elsif args.length == 1 and args.first.type == :const
236
250
  # accommodation for JavaScript like new syntax w/o argument list
237
251
  parse s(:attr, args.first, :new), @state
238
- elsif
252
+ elsif
239
253
  args.length == 2 and [:send, :const].include? args.first.type and
240
254
  args.last.type == :def and args.last.children.first == nil
241
255
  then
@@ -269,7 +283,7 @@ module Ruby2JS
269
283
  parse ast.updated(:lvasgn, [method]), @state
270
284
  end
271
285
  elsif args.any? {|arg| arg.type == :splat} and not es2015
272
- parse s(:send, s(:attr, receiver, method), :apply,
286
+ parse s(:send, s(:attr, receiver, method), :apply,
273
287
  (receiver || s(:nil)), s(:array, *args))
274
288
  else
275
289
  (group_receiver ? group(receiver) : parse(receiver))
@@ -313,47 +327,98 @@ module Ruby2JS
313
327
  parse expr
314
328
  end
315
329
 
316
- # do string concatenation when possible
317
- def collapse_strings(node)
318
- left = node.children[0]
319
- return node unless left
320
- right = node.children[2]
321
-
322
- # recursively evaluate left hand side
323
- if
324
- left.type == :send and left.children.length == 3 and
325
- left.children[1] == :+
326
- then
327
- left = collapse_strings(left)
328
- end
330
+ # do string concatenation when possible
331
+ def collapse_strings(node)
332
+ left = node.children[0]
333
+ return node unless left
334
+ right = node.children[2]
335
+
336
+ # recursively evaluate left hand side
337
+ if
338
+ left.type == :send and left.children.length == 3 and
339
+ left.children[1] == :+
340
+ then
341
+ left = collapse_strings(left)
342
+ end
329
343
 
330
- # recursively evaluate right hand side
331
- if
332
- right.type == :send and right.children.length == 3 and
333
- right.children[1] == :+
334
- then
335
- right = collapse_strings(right)
336
- end
344
+ # recursively evaluate right hand side
345
+ if
346
+ right.type == :send and right.children.length == 3 and
347
+ right.children[1] == :+
348
+ then
349
+ right = collapse_strings(right)
350
+ end
337
351
 
338
- # if left and right are both strings, perform concatenation
339
- if [:dstr, :str].include? left.type and [:dstr, :str].include? right.type
340
- if left.type == :str and right.type == :str
341
- return left.updated nil,
342
- [left.children.first + right.children.first]
352
+ # if left and right are both strings, perform concatenation
353
+ if [:dstr, :str].include? left.type and [:dstr, :str].include? right.type
354
+ if left.type == :str and right.type == :str
355
+ return left.updated nil,
356
+ [left.children.first + right.children.first]
357
+ else
358
+ left = s(:dstr, left) if left.type == :str
359
+ right = s(:dstr, right) if right.type == :str
360
+ return left.updated(nil, left.children + right.children)
361
+ end
362
+ end
363
+
364
+ # if left and right are unchanged, return original node; otherwise
365
+ # return node modified to include new left and/or right hand sides.
366
+ if left == node.children[0] and right == node.children[2]
367
+ return node
343
368
  else
344
- left = s(:dstr, left) if left.type == :str
345
- right = s(:dstr, right) if right.type == :str
346
- return left.updated(nil, left.children + right.children)
369
+ return node.updated(nil, [left, :+, right])
347
370
  end
348
371
  end
349
372
 
350
- # if left and right are unchanged, return original node; otherwise
351
- # return node modified to include new left and/or right hand sides.
352
- if left == node.children[0] and right == node.children[2]
353
- return node
354
- else
355
- return node.updated(nil, [left, :+, right])
373
+ def range_to_array(node)
374
+ start, finish = node.children
375
+ if start.type == :int and start.children.first == 0
376
+ # Ranges which start from 0 can be achieved with more simpler code
377
+ if finish.type == :int
378
+ # output cleaner code if we know the value already
379
+ length = finish.children.first + (node.type == :irange ? 1 : 0)
380
+ else
381
+ # If this is variable we need to fix indexing by 1 in js
382
+ length = "#{finish.children.last}" + (node.type == :irange ? "+1" : "")
383
+ end
384
+
385
+ if es2015
386
+ return put "[...Array(#{length}).keys()]"
387
+ else
388
+ return put "Array.apply(null, {length: #{length}}).map(Function.call, Number)"
389
+ end
390
+ else
391
+ # Use .compact because the first argument is nil with variables
392
+ # This way the first value is always set
393
+ start_value = start.children.compact.first
394
+ finish_value = finish.children.compact.first
395
+ if start.type == :int and finish.type == :int
396
+ length = finish_value - start_value + (node.type == :irange ? 1 : 0)
397
+ else
398
+ length = "(#{finish_value}-#{start_value}" + (node.type == :irange ? "+1" : "") + ")"
399
+ end
400
+
401
+ # Avoid of using same variables in the map as used in the irange or elsewhere in this code
402
+ # Ruby2js only allows dollar sign in beginning of variable so i$ is safe
403
+ if @vars.include? :idx or start_value == :idx or finish_value == :idx
404
+ index_var = 'i$'
405
+ else
406
+ index_var = 'idx'
407
+ end
408
+
409
+ if es2015
410
+ # Use _ because it's normal convention in JS for variable which is not used at all
411
+ if @vars.include? :_ or start_value == :_ or finish_value == :_
412
+ blank = '_$'
413
+ else
414
+ blank = '_'
415
+ end
416
+
417
+ return put "Array.from({length: #{length}}, (#{blank}, #{index_var}) => #{index_var}+#{start_value})"
418
+ else
419
+ return put "Array.apply(null, {length: #{length}}).map(Function.call, Number).map(function (#{index_var}) { return #{index_var}+#{start_value} })"
420
+ end
421
+ end
356
422
  end
357
423
  end
358
- end
359
424
  end
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2018
4
+ Ruby2JS.eslevel_default = 2018
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2018'
2
+
3
+ Ruby2JS.strict_default = true
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2019
4
+ Ruby2JS.eslevel_default = 2019
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2019'
2
+
3
+ Ruby2JS.strict_default = true
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2020
4
+ Ruby2JS.eslevel_default = 2020
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2020'
2
+
3
+ Ruby2JS.strict_default = true
@@ -35,6 +35,27 @@ module Ruby2JS
35
35
  elsif method == :keys and args.length == 0 and node.is_method?
36
36
  process S(:send, s(:const, nil, :Object), :keys, target)
37
37
 
38
+ elsif method == :merge
39
+ args.unshift target
40
+
41
+ if es2015
42
+ if es2018
43
+ process S(:hash, *args.map {|arg| s(:kwsplat, arg)})
44
+ else
45
+ process S(:send, s(:const, nil, :Object), :assign, s(:hash),
46
+ *args)
47
+ end
48
+ else
49
+ copy = [s(:gvasgn, :$$, s(:hash))]
50
+
51
+ s(:send, s(:block, s(:send, nil, :lambda), s(:args),
52
+ s(:begin, *copy, *args.map {|modname|
53
+ s(:for, s(:lvasgn, :$_), modname,
54
+ s(:send, s(:gvar, :$$), :[]=,
55
+ s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
56
+ }, s(:return, s(:gvar, :$$)))), :[])
57
+ end
58
+
38
59
  elsif method == :merge!
39
60
  if es2015
40
61
  process S(:send, s(:const, nil, :Object), :assign, target, *args)
@@ -367,6 +388,19 @@ module Ruby2JS
367
388
  elsif es2017 and method==:ljust
368
389
  process node.updated(nil, [target, :padEnd, *args])
369
390
 
391
+ elsif es2019 and method==:flatten and args.length == 0
392
+ process node.updated(nil, [target, :flat, s(:lvar, :Infinity)])
393
+
394
+ elsif es2019 and method==:to_h and args.length==0
395
+ process node.updated(nil, [s(:const, nil, :Object), :fromEntries,
396
+ target])
397
+
398
+ elsif es2019 and method==:rstrip
399
+ process node.updated(nil, [target, :trimStart, *args])
400
+
401
+ elsif es2019 and method==:lstrip
402
+ process node.updated(nil, [target, :trimEnd, *args])
403
+
370
404
  elsif method == :class and args.length==0 and not node.is_method?
371
405
  process node.updated(:attr, [target, :constructor])
372
406
 
@@ -1,8 +1,8 @@
1
1
  module Ruby2JS
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
- MINOR = 0
5
- TINY = 15
4
+ MINOR = 1
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby2js
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.15
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Ruby
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-22 00:00:00.000000000 Z
11
+ date: 2019-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -93,6 +93,12 @@ files:
93
93
  - lib/ruby2js/es2016/strict.rb
94
94
  - lib/ruby2js/es2017.rb
95
95
  - lib/ruby2js/es2017/strict.rb
96
+ - lib/ruby2js/es2018.rb
97
+ - lib/ruby2js/es2018/strict.rb
98
+ - lib/ruby2js/es2019.rb
99
+ - lib/ruby2js/es2019/strict.rb
100
+ - lib/ruby2js/es2020.rb
101
+ - lib/ruby2js/es2020/strict.rb
96
102
  - lib/ruby2js/execjs.rb
97
103
  - lib/ruby2js/filter.rb
98
104
  - lib/ruby2js/filter/camelCase.rb