ruby2js 3.0.15 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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