ruby2js 3.5.3 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -665
  3. data/lib/ruby2js.rb +60 -15
  4. data/lib/ruby2js/converter.rb +39 -3
  5. data/lib/ruby2js/converter/args.rb +6 -1
  6. data/lib/ruby2js/converter/assign.rb +159 -0
  7. data/lib/ruby2js/converter/begin.rb +7 -2
  8. data/lib/ruby2js/converter/case.rb +7 -2
  9. data/lib/ruby2js/converter/class.rb +80 -21
  10. data/lib/ruby2js/converter/class2.rb +107 -33
  11. data/lib/ruby2js/converter/def.rb +7 -3
  12. data/lib/ruby2js/converter/dstr.rb +8 -3
  13. data/lib/ruby2js/converter/for.rb +1 -1
  14. data/lib/ruby2js/converter/hash.rb +28 -6
  15. data/lib/ruby2js/converter/hide.rb +13 -0
  16. data/lib/ruby2js/converter/if.rb +10 -2
  17. data/lib/ruby2js/converter/import.rb +19 -4
  18. data/lib/ruby2js/converter/kwbegin.rb +9 -2
  19. data/lib/ruby2js/converter/literal.rb +14 -2
  20. data/lib/ruby2js/converter/logical.rb +1 -1
  21. data/lib/ruby2js/converter/module.rb +41 -4
  22. data/lib/ruby2js/converter/next.rb +10 -2
  23. data/lib/ruby2js/converter/opasgn.rb +8 -0
  24. data/lib/ruby2js/converter/redo.rb +14 -0
  25. data/lib/ruby2js/converter/return.rb +2 -1
  26. data/lib/ruby2js/converter/send.rb +73 -8
  27. data/lib/ruby2js/converter/vasgn.rb +5 -0
  28. data/lib/ruby2js/converter/while.rb +1 -1
  29. data/lib/ruby2js/converter/whilepost.rb +1 -1
  30. data/lib/ruby2js/converter/xstr.rb +2 -3
  31. data/lib/ruby2js/demo.rb +53 -0
  32. data/lib/ruby2js/es2022.rb +5 -0
  33. data/lib/ruby2js/es2022/strict.rb +3 -0
  34. data/lib/ruby2js/filter.rb +9 -1
  35. data/lib/ruby2js/filter/active_functions.rb +44 -0
  36. data/lib/ruby2js/filter/camelCase.rb +6 -3
  37. data/lib/ruby2js/filter/cjs.rb +2 -0
  38. data/lib/ruby2js/filter/esm.rb +118 -26
  39. data/lib/ruby2js/filter/functions.rb +137 -109
  40. data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
  41. data/lib/ruby2js/filter/node.rb +58 -14
  42. data/lib/ruby2js/filter/nokogiri.rb +12 -12
  43. data/lib/ruby2js/filter/react.rb +182 -57
  44. data/lib/ruby2js/filter/require.rb +102 -11
  45. data/lib/ruby2js/filter/return.rb +13 -1
  46. data/lib/ruby2js/filter/stimulus.rb +187 -0
  47. data/lib/ruby2js/jsx.rb +309 -0
  48. data/lib/ruby2js/namespace.rb +75 -0
  49. data/lib/ruby2js/serializer.rb +19 -12
  50. data/lib/ruby2js/sprockets.rb +40 -0
  51. data/lib/ruby2js/version.rb +3 -3
  52. data/ruby2js.gemspec +2 -2
  53. metadata +23 -13
  54. data/lib/ruby2js/filter/esm_migration.rb +0 -72
  55. data/lib/ruby2js/rails.rb +0 -63
@@ -6,7 +6,7 @@ module Ruby2JS
6
6
  # (arg :x)
7
7
  # (...)
8
8
 
9
- handle :def, :defm, :async do |name, args, body=nil|
9
+ handle :def, :defm, :async, :deff do |name, args, body=nil|
10
10
  body ||= s(:begin)
11
11
 
12
12
  add_implicit_block = false
@@ -106,7 +106,7 @@ module Ruby2JS
106
106
  # es2015 fat arrow support
107
107
  if \
108
108
  not name and es2015 and @state != :method and @ast.type != :defm and
109
- not @prop
109
+ @ast.type != :deff and not @prop
110
110
  then
111
111
  expr = body
112
112
  expr = expr.children.first while expr.type == :autoreturn
@@ -118,6 +118,10 @@ module Ruby2JS
118
118
  if EXPRESSIONS.include? expr.type
119
119
  if expr.type == :send and expr.children[0..1] == [nil, :raise]
120
120
  style = :statement
121
+ elsif expr.type == :send and expr.children.length == 2 and
122
+ expr.children.first == nil and @rbstack.last and
123
+ @rbstack.last[expr.children[1]]&.type == :autobind
124
+ style = :statement
121
125
  else
122
126
  style = :expression
123
127
  end
@@ -131,7 +135,7 @@ module Ruby2JS
131
135
  style = :statement
132
136
  end
133
137
 
134
- if args.children.length == 1 and style == :expression
138
+ if args.children.length == 1 and args.children.first.type == :arg and style == :expression
135
139
  parse args; put ' => '
136
140
  else
137
141
  put '('; parse args; put ') => '
@@ -10,6 +10,11 @@ module Ruby2JS
10
10
  # (...))
11
11
 
12
12
  handle :dstr, :dsym do |*children|
13
+ if @state == :expression and children.empty?
14
+ puts '""'
15
+ return
16
+ end
17
+
13
18
  if es2015
14
19
  # gather length of string parts; if long enough, newlines will
15
20
  # not be escaped (poor man's HEREDOC)
@@ -26,7 +31,7 @@ module Ruby2JS
26
31
  else
27
32
  put str
28
33
  end
29
- else
34
+ elsif child != s(:begin)
30
35
  put '${'
31
36
  parse child
32
37
  put '}'
@@ -40,8 +45,8 @@ module Ruby2JS
40
45
  children.each_with_index do |child, index|
41
46
  put ' + ' unless index == 0
42
47
 
43
- if child.type == :begin and child.children.length == 1
44
- child = child.children.first
48
+ if child.type == :begin and child.children.length <= 1
49
+ child = child.children.first || s(:str, '')
45
50
  end
46
51
 
47
52
  if child.type == :send
@@ -28,7 +28,7 @@ module Ruby2JS
28
28
  else
29
29
  put (@ast.type==:for_of ? ' of ' : ' in '); parse expression;
30
30
  end
31
- puts ') {'; scope block; sput '}'
31
+ puts ') {'; redoable block; sput '}'
32
32
  ensure
33
33
  @next_token = next_token
34
34
  @vars = vars if es2015
@@ -7,6 +7,24 @@ module Ruby2JS
7
7
  # (str "value")))
8
8
 
9
9
  handle :hash do |*pairs|
10
+ if not es2018 and pairs.any? {|pair| pair.type == :kwsplat}
11
+ groups = []
12
+ pending = []
13
+ while not pairs.empty?
14
+ pair = pairs.shift
15
+ if pair.type != :kwsplat
16
+ pending << pair
17
+ else
18
+ groups << s(:hash, *pending) unless pending.empty?
19
+ groups << pair.children.first
20
+ pending = []
21
+ end
22
+ end
23
+ groups << s(:hash, *pending) unless pending.empty?
24
+ parse s(:assign, s(:hash), *groups)
25
+ return
26
+ end
27
+
10
28
  compact do
11
29
  singleton = pairs.length <= 1
12
30
 
@@ -24,7 +42,7 @@ module Ruby2JS
24
42
  pairs.unshift(*node.children.first.children)
25
43
  index = 0
26
44
  else
27
- puts '...'; parse node.children.first
45
+ put '...'; parse node.children.first
28
46
  end
29
47
 
30
48
  next
@@ -73,7 +91,7 @@ module Ruby2JS
73
91
  if right.type == :hash
74
92
  right.children.each do |pair|
75
93
  next unless Parser::AST::Node === pair.children.last
76
- if [:block, :def, :async].include? pair.children.last.type
94
+ if %i[block def defm async].include? pair.children.last.type
77
95
  if @comments[pair.children.last]
78
96
  (puts ''; singleton = false) if singleton
79
97
  comments(pair.children.last).each do |comment|
@@ -120,22 +138,26 @@ module Ruby2JS
120
138
 
121
139
  if \
122
140
  anonfn and
123
- left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
141
+ left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
124
142
  then
125
143
  @prop = left.children.first
126
144
  parse right, :method
127
145
  elsif \
128
- es2015 and left.type == :sym and right.type == :lvar and
129
- left.children == right.children
146
+ es2015 and left.type == :sym and (right.type == :lvar or
147
+ (right.type == :send and right.children.first == nil)) and
148
+ left.children.last == right.children.last
130
149
  then
131
150
  parse right
151
+ elsif right.type == :defm and %i[sym str].include? left.type and es2015
152
+ @prop = left.children.first.to_s
153
+ parse right
132
154
  else
133
155
  if not [:str, :sym].include? left.type and es2015
134
156
  put '['
135
157
  parse left
136
158
  put ']'
137
159
  elsif \
138
- left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
160
+ left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
139
161
  then
140
162
  put left.children.first
141
163
  else
@@ -0,0 +1,13 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (hide, ...)
5
+
6
+ handle :hide do |*nodes|
7
+ capture {parse_all(*nodes)}
8
+
9
+ @lines.pop if @state == :statement and @lines.last == []
10
+ @lines.last.pop if @lines.last.last == @sep
11
+ end
12
+ end
13
+ end
@@ -64,10 +64,18 @@ module Ruby2JS
64
64
  if else_block.type == :begin
65
65
  else_block = s(:xnode, '', *else_block.children)
66
66
  end
67
+ else
68
+ if then_block.type == :begin
69
+ then_block = s(:kwbegin, then_block)
70
+ end
71
+
72
+ if else_block.type == :begin
73
+ else_block = s(:kwbegin, else_block)
74
+ end
67
75
  end
68
76
 
69
- parse condition; put ' ? '; parse then_block
70
- put ' : '; parse else_block
77
+ parse condition; put ' ? '; parse then_block, @state
78
+ put ' : '; parse else_block, @state
71
79
  end
72
80
  end
73
81
  end
@@ -28,14 +28,29 @@ module Ruby2JS
28
28
  put path.inspect
29
29
  else
30
30
  # import (x) from "file.js"
31
- default_import = !args.first.is_a?(Array) && [:const, :send, :attr].include?(args.first.type)
31
+ default_import = !args.first.is_a?(Array) && %i[const send attr str].include?(args.first.type)
32
+
33
+ if default_import and args.length > 1
34
+ parse args.shift
35
+ put ', '
36
+ default_import = false
37
+ end
38
+
32
39
  args = args.first if args.first.is_a?(Array)
33
40
 
41
+ if args.first.type == :array
42
+ args = args.first.children
43
+ end
44
+
34
45
  # handle the default name or { ConstA, Const B } portion
35
46
  put "{ " unless default_import
36
47
  args.each_with_index do |arg, index|
37
48
  put ', ' unless index == 0
38
- parse arg
49
+ if arg.type == :str
50
+ put arg.children.first # useful for '*'
51
+ else
52
+ parse arg
53
+ end
39
54
  end
40
55
  put " }" unless default_import
41
56
 
@@ -43,7 +58,7 @@ module Ruby2JS
43
58
 
44
59
  # should there be an as clause? e.g., import React as *
45
60
  if path.is_a?(Array) && !path[0].is_a?(String) && path[0].type == :pair && path[0].children[0].children[0] == :as
46
- put " as #{path[0].children[1].children[0]}"
61
+ put " as #{path[0].children[1].children.last}"
47
62
 
48
63
  # advance to the next kwarg, aka from
49
64
  from_kwarg_position = 1
@@ -82,7 +97,7 @@ module Ruby2JS
82
97
  elsif node.respond_to?(:type) && node.children[1] == :default
83
98
  put 'default '
84
99
  args[0] = node.children[2]
85
- elsif node.respond_to?(:type) && node.type == :lvasgn
100
+ elsif node.respond_to?(:type) && [:lvasgn, :casgn].include?(node.type)
86
101
  if node.children[0] == :default
87
102
  put 'default '
88
103
  args[0] = node.children[1]
@@ -6,7 +6,7 @@ module Ruby2JS
6
6
  # (resbody nil nil
7
7
  # (send nil :b)) nil)
8
8
  handle :rescue do |*statements|
9
- parse_all s(:kwbegin, s(:rescue, *statements))
9
+ parse s(:kwbegin, s(:rescue, *statements)), @state
10
10
  end
11
11
 
12
12
  # (kwbegin
@@ -19,7 +19,14 @@ module Ruby2JS
19
19
 
20
20
  handle :kwbegin do |*children|
21
21
  block = children.first
22
- if block.type == :ensure
22
+
23
+ if @state == :expression
24
+ parse s(:send, s(:block, s(:send, nil, :proc), s(:args),
25
+ s(:begin, s(:autoreturn, *children))), :[])
26
+ return
27
+ end
28
+
29
+ if block&.type == :ensure
23
30
  block, finally = block.children
24
31
  else
25
32
  finally = nil
@@ -5,12 +5,24 @@ module Ruby2JS
5
5
  # (float 1.1)
6
6
  # (str "1"))
7
7
 
8
- handle :int, :float, :str do |value|
8
+ handle :str do |value|
9
9
  put value.inspect
10
10
  end
11
11
 
12
+ handle :int, :float do |value|
13
+ put number_format(value)
14
+ end
15
+
12
16
  handle :octal do |value|
13
- put '0' + value.to_s(8)
17
+ put '0' + number_format(value.to_s(8))
18
+ end
19
+
20
+ def number_format(number)
21
+ return number.to_s unless es2021
22
+ parts = number.to_s.split('.')
23
+ parts[0] = parts[0].gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1_")
24
+ parts[1] = parts[1].gsub(/(\d\d\d)(?=\d)/, "\\1_") if parts[1]
25
+ parts.join('.')
14
26
  end
15
27
  end
16
28
  end
@@ -57,7 +57,7 @@ module Ruby2JS
57
57
  else
58
58
  group = LOGICAL.include?( expr.type ) &&
59
59
  operator_index( :not ) < operator_index( expr.type )
60
- group = true if expr and expr.type == :begin
60
+ group = true if expr and %i[begin in?].include? expr.type
61
61
 
62
62
  put '!'; put '(' if group; parse expr; put ')' if group
63
63
  end
@@ -4,14 +4,43 @@ module Ruby2JS
4
4
  # (module
5
5
  # (const nil :A)
6
6
  # (...)
7
+ #
8
+ # Note: modules_hash is an anonymous modules as a value in a hash; the
9
+ # name has already been output so should be ignored other than
10
+ # in determining the namespace.
11
+
12
+ handle :module, :module_hash do |name, *body|
13
+ extend = @namespace.enter(name)
14
+
15
+ if body == [nil]
16
+ if @ast.type == :module and not extend
17
+ parse @ast.updated(:casgn, [*name.children, s(:hash)])
18
+ else
19
+ parse @ast.updated(:hash, [])
20
+ end
21
+
22
+ @namespace.leave
23
+ return
24
+ end
7
25
 
8
- handle :module do |name, *body|
9
26
  while body.length == 1 and body.first.type == :begin
10
27
  body = body.first.children
11
28
  end
12
29
 
13
- if body.length > 0 and body.all? {|child| child.type == :def}
14
- parse @ast.updated(:class_module, [name, nil, *body])
30
+ if body.length > 0 and body.all? {|child|
31
+ %i[def module].include? child.type or
32
+ (es2015 and child.type == :class and child.children[1] == nil)}
33
+
34
+ if extend
35
+ parse s(:assign, name, @ast.updated(:class_module,
36
+ [nil, nil, *body])), :statement
37
+ elsif @ast.type == :module_hash
38
+ parse @ast.updated(:class_module, [nil, nil, *body])
39
+ else
40
+ parse @ast.updated(:class_module, [name, nil, *body])
41
+ end
42
+
43
+ @namespace.leave
15
44
  return
16
45
  end
17
46
 
@@ -42,6 +71,8 @@ module Ruby2JS
42
71
  symbols << node.children.first
43
72
  elsif node.type == :class and node.children.first.children.first == nil
44
73
  symbols << node.children.first.children.last
74
+ elsif node.type == :module
75
+ symbols << node.children.first.children.last
45
76
  end
46
77
  end
47
78
 
@@ -50,11 +81,17 @@ module Ruby2JS
50
81
 
51
82
  body = s(:send, s(:block, s(:send, nil, :proc), s(:args),
52
83
  s(:begin, *body)), :[])
53
- if name.children.first == nil
84
+ if not name
85
+ parse body
86
+ elsif extend
87
+ parse s(:assign, name, body)
88
+ elsif name.children.first == nil
54
89
  parse s(:lvasgn, name.children.last, body)
55
90
  else
56
91
  parse s(:send, name.children.first, "#{name.children.last}=", body)
57
92
  end
93
+
94
+ @namespace.leave
58
95
  end
59
96
  end
60
97
  end
@@ -5,8 +5,16 @@ module Ruby2JS
5
5
  # (int 1))
6
6
 
7
7
  handle :next do |n=nil|
8
- raise Error.new("next argument #{ n.inspect }", @ast) if n
9
- put @next_token.to_s
8
+ if @next_token == :return
9
+ put 'return'
10
+ if n
11
+ put ' '
12
+ parse n
13
+ end
14
+ else
15
+ raise Error.new("next argument #{ n.inspect }", @ast) if n
16
+ put @next_token.to_s
17
+ end
10
18
  end
11
19
  end
12
20
  end
@@ -12,6 +12,14 @@ module Ruby2JS
12
12
  var = s(:lvar, var.children.first) if var.type == :lvasgn
13
13
  var = s(:cvar, var.children.first) if var.type == :cvasgn
14
14
 
15
+ if var.type == :lvar
16
+ name = var.children.first
17
+ receiver = @rbstack.map {|rb| rb[name]}.compact.last
18
+ if receiver
19
+ var = s(:attr, nil, name)
20
+ end
21
+ end
22
+
15
23
  if \
16
24
  [:+, :-].include?(op) and value.type==:int and
17
25
  (value.children==[1] or value.children==[-1])
@@ -0,0 +1,14 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (redo)
5
+
6
+ handle :redo do
7
+ unless @redoable and @next_token == :continue
8
+ raise Error.new("redo outside of loop", @ast)
9
+ end
10
+
11
+ put "redo$ = true#{@sep}continue"
12
+ end
13
+ end
14
+ end
@@ -14,7 +14,8 @@ module Ruby2JS
14
14
 
15
15
  EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send, :attr,
16
16
  :str, :sym, :dstr, :dsym, :cvar, :ivar, :zsuper, :super, :or, :and,
17
- :block, :const, :true, :false, :xnode, :taglit, :self ]
17
+ :block, :const, :true, :false, :xnode, :taglit, :self,
18
+ :op_asgn, :and_asgn, :or_asgn ]
18
19
 
19
20
  handle :autoreturn do |*statements|
20
21
  return if statements == [nil]
@@ -9,11 +9,12 @@ module Ruby2JS
9
9
  # (sendw nil :puts
10
10
  # (int 1))
11
11
 
12
- # Note: attr, sendw, and await are only generated by filters. Attr forces
12
+ # Note: attr, sendw, send!, and await are only generated by filters. Attr forces
13
13
  # interpretation as an attribute vs a function call with zero parameters.
14
+ # send! forces interpretation as a method call even with zero parameters.
14
15
  # Sendw forces parameters to be placed on separate lines.
15
16
 
16
- handle :send, :sendw, :await, :attr, :call do |receiver, method, *args|
17
+ handle :send, :sendw, :send!, :await, :attr, :call do |receiver, method, *args|
17
18
  ast = @ast
18
19
 
19
20
  if \
@@ -40,12 +41,26 @@ module Ruby2JS
40
41
  # strip '!' and '?' decorations
41
42
  method = method.to_s[0..-2] if method =~ /\w[!?]$/
42
43
 
44
+ # anonymous class
45
+ if method == :new and receiver and receiver.children == [nil, :Class] and
46
+ args.last.type == :def and args.last.children.first == nil
47
+
48
+ parent = (args.length > 1) ? args.first : nil
49
+
50
+ if es2015
51
+ return parse s(:class2, nil, parent, *args.last.children[2..-1])
52
+ else
53
+ return parse s(:kwbegin, s(:class, s(:const, nil, :$$), parent,
54
+ *args.last.children[2..-1]), s(:const, nil, :$$))
55
+ end
56
+ end
57
+
43
58
  # three ways to define anonymous functions
44
59
  if method == :new and receiver and receiver.children == [nil, :Proc]
45
60
  return parse args.first, @state
46
61
 
47
62
  elsif not receiver and [:lambda, :proc].include? method
48
- if method == :lambda
63
+ if method == :lambda and @state != :statement
49
64
  return parse s(args.first.type, *args.first.children[0..-2],
50
65
  s(:autoreturn, args.first.children[-1])), @state
51
66
  else
@@ -60,10 +75,17 @@ module Ruby2JS
60
75
  (es2015 || @state == :statement ? group(receiver) : parse(receiver))
61
76
  put '('; parse_all(*args, join: ', '); put ')'
62
77
  return
78
+ elsif not t2 and m2 == :async and args2.length == 0
79
+ put '('; parse receiver; put ')()'
80
+ return
63
81
  end
64
82
  end
65
83
 
66
84
  # async/await support
85
+ # map "await x do...end" to "await x {...}" due to precedence rules
86
+ if method == :await and es2017 and receiver == nil and args.length == 2 and args[1].type == :def
87
+ args = [s(:block, args.first, *args.last.children[1..-1])]
88
+ end
67
89
  if es2017 and receiver == nil and args.length == 1
68
90
  if method == :async
69
91
  if args.first.type == :def
@@ -74,6 +96,15 @@ module Ruby2JS
74
96
  # async def o.m(x) {...}
75
97
  return parse args.first.updated :asyncs
76
98
 
99
+ elsif args.first.type == :send and
100
+ args.first.children.first.type == :block and
101
+ args.first.children.last == :[]
102
+
103
+ put '(async '
104
+ parse args.first.children.first, :statement
105
+ put ')()'
106
+ return
107
+
77
108
  elsif args.first.type == :block
78
109
  block = args.first
79
110
 
@@ -117,8 +148,13 @@ module Ruby2JS
117
148
 
118
149
  # resolve anonymous receivers against rbstack
119
150
  receiver ||= @rbstack.map {|rb| rb[method]}.compact.last
151
+ autobind = nil
120
152
 
121
153
  if receiver
154
+ if receiver.type == :autobind
155
+ autobind = receiver = receiver.children.first
156
+ end
157
+
122
158
  group_receiver = receiver.type == :send &&
123
159
  op_index < operator_index( receiver.children[1] ) if receiver
124
160
  group_receiver ||= GROUP_OPERATORS.include? receiver.type
@@ -137,6 +173,8 @@ module Ruby2JS
137
173
  group_target ||= GROUP_OPERATORS.include? target.type
138
174
  end
139
175
 
176
+ put 'await ' if @ast.type == :await
177
+
140
178
  if method == :!
141
179
  parse s(:not, receiver)
142
180
 
@@ -152,7 +190,7 @@ module Ruby2JS
152
190
  end
153
191
 
154
192
  elsif method == :[]=
155
- parse receiver
193
+ (group_receiver ? group(receiver) : parse(receiver))
156
194
  if \
157
195
  args.length == 2 and [:str, :sym].include? args.first.type and
158
196
  args.first.children.first.to_s =~ /^[a-zA-Z]\w*$/
@@ -175,9 +213,15 @@ module Ruby2JS
175
213
  receiver.type == :send and
176
214
  receiver.children[1] == :+@ and
177
215
  Parser::AST::Node === receiver.children[0] and
178
- receiver.children[0].type == :class
216
+ %i(class module).include? receiver.children[0].type
179
217
  then
180
- parse receiver.children[0].updated(:class_extend)
218
+ if receiver.children[0].type == :class
219
+ parse receiver.children[0].updated(:class_extend)
220
+ else
221
+ mod = receiver.children[0]
222
+ parse s(:assign, mod.children[0],
223
+ mod.updated(nil, [nil, *mod.children[1..-1]]))
224
+ end
181
225
  else
182
226
  put method.to_s[0]; parse receiver
183
227
  end
@@ -277,10 +321,23 @@ module Ruby2JS
277
321
  elsif method == :typeof and receiver == nil
278
322
  put 'typeof '; parse args.first
279
323
 
324
+ elsif ast.children[1] == :is_a? and receiver and args.length == 1
325
+ parse receiver; put ' instanceof '; parse args.first
326
+
327
+ elsif ast.children[1] == :kind_of? and receiver and args.length == 1
328
+ parse receiver; put ' instanceof '; parse args.first
329
+
330
+ elsif ast.children[1] == :instance_of? and receiver and args.length == 1
331
+ parse s(:send, s(:attr, receiver, :constructor), :==, args.first)
332
+
280
333
  else
281
- put 'await ' if @ast.type == :await
334
+ if method == :bind and receiver&.type == :send
335
+ if receiver.children.length == 2 and receiver.children.first == nil
336
+ receiver = receiver.updated(:attr) # prevent autobind
337
+ end
338
+ end
282
339
 
283
- if not ast.is_method?
340
+ if not ast.is_method? and ast.type != :send!
284
341
  if receiver
285
342
  (group_receiver ? group(receiver) : parse(receiver))
286
343
  put ".#{ method }"
@@ -302,6 +359,14 @@ module Ruby2JS
302
359
  compact { puts "("; parse_all(*args, join: ",#@ws"); sput ')' }
303
360
  end
304
361
  end
362
+
363
+ if autobind and not ast.is_method? and ast.type != :attr
364
+ if @state == :statement
365
+ put '()'
366
+ else
367
+ put '.bind('; parse(autobind); put ')'
368
+ end
369
+ end
305
370
  end
306
371
  end
307
372