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 +4 -4
- data/README.md +45 -1
- data/lib/ruby2js.rb +15 -1
- data/lib/ruby2js/converter.rb +12 -0
- data/lib/ruby2js/converter/args.rb +30 -0
- data/lib/ruby2js/converter/casgn.rb +3 -2
- data/lib/ruby2js/converter/class2.rb +82 -7
- data/lib/ruby2js/converter/cvar.rb +5 -3
- data/lib/ruby2js/converter/cvasgn.rb +5 -3
- data/lib/ruby2js/converter/hash.rb +19 -3
- data/lib/ruby2js/converter/ivar.rb +2 -0
- data/lib/ruby2js/converter/ivasgn.rb +1 -1
- data/lib/ruby2js/converter/kwbegin.rb +16 -2
- data/lib/ruby2js/converter/send.rb +113 -48
- data/lib/ruby2js/es2018.rb +5 -0
- data/lib/ruby2js/es2018/strict.rb +3 -0
- data/lib/ruby2js/es2019.rb +5 -0
- data/lib/ruby2js/es2019/strict.rb +3 -0
- data/lib/ruby2js/es2020.rb +5 -0
- data/lib/ruby2js/es2020/strict.rb +3 -0
- data/lib/ruby2js/filter/functions.rb +34 -0
- data/lib/ruby2js/version.rb +2 -2
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0d87e8dda198550df6b79aa5a14e033ff6180f6057998d63252a41011d04735
|
4
|
+
data.tar.gz: 1869aa7759c6b40137258b3a87c0c250944940753cc5d0814a0e027bd88068dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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"
|
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
|
data/lib/ruby2js/converter.rb
CHANGED
@@ -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
|
-
|
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.
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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('@@',
|
14
|
+
put var.to_s.sub('@@', "this.#{prefix}")
|
13
15
|
else
|
14
|
-
put var.to_s.sub('@@',
|
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('@@',
|
16
|
+
put var.to_s.sub('@@', "this.#{prefix}")
|
15
17
|
else
|
16
|
-
put var.to_s.sub('@@',
|
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
|
-
|
16
|
-
|
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
|
@@ -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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
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
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
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
|
-
|
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
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
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
|
@@ -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
|
|
data/lib/ruby2js/version.rb
CHANGED
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
|
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-
|
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
|