ruby2js 3.6.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -7
  3. data/lib/ruby2js.rb +32 -9
  4. data/lib/ruby2js/converter.rb +8 -2
  5. data/lib/ruby2js/converter/assign.rb +159 -0
  6. data/lib/ruby2js/converter/begin.rb +7 -2
  7. data/lib/ruby2js/converter/case.rb +7 -2
  8. data/lib/ruby2js/converter/class.rb +77 -21
  9. data/lib/ruby2js/converter/class2.rb +39 -11
  10. data/lib/ruby2js/converter/def.rb +6 -2
  11. data/lib/ruby2js/converter/dstr.rb +8 -3
  12. data/lib/ruby2js/converter/hash.rb +9 -5
  13. data/lib/ruby2js/converter/hide.rb +13 -0
  14. data/lib/ruby2js/converter/if.rb +10 -2
  15. data/lib/ruby2js/converter/import.rb +18 -3
  16. data/lib/ruby2js/converter/kwbegin.rb +9 -2
  17. data/lib/ruby2js/converter/literal.rb +2 -2
  18. data/lib/ruby2js/converter/module.rb +37 -5
  19. data/lib/ruby2js/converter/opasgn.rb +8 -0
  20. data/lib/ruby2js/converter/send.rb +41 -2
  21. data/lib/ruby2js/converter/vasgn.rb +5 -0
  22. data/lib/ruby2js/demo.rb +53 -0
  23. data/lib/ruby2js/es2022.rb +5 -0
  24. data/lib/ruby2js/es2022/strict.rb +3 -0
  25. data/lib/ruby2js/filter.rb +9 -1
  26. data/lib/ruby2js/filter/active_functions.rb +1 -0
  27. data/lib/ruby2js/filter/cjs.rb +2 -0
  28. data/lib/ruby2js/filter/esm.rb +44 -10
  29. data/lib/ruby2js/filter/functions.rb +84 -95
  30. data/lib/ruby2js/filter/{wunderbar.rb → jsx.rb} +29 -7
  31. data/lib/ruby2js/filter/react.rb +191 -56
  32. data/lib/ruby2js/filter/require.rb +100 -5
  33. data/lib/ruby2js/filter/return.rb +13 -1
  34. data/lib/ruby2js/filter/stimulus.rb +185 -0
  35. data/lib/ruby2js/jsx.rb +291 -0
  36. data/lib/ruby2js/namespace.rb +75 -0
  37. data/lib/ruby2js/version.rb +3 -3
  38. metadata +12 -4
@@ -18,7 +18,9 @@ module Ruby2JS
18
18
  node.type == :def and node.children.first == :method_missing
19
19
  end
20
20
 
21
- if name.type == :const and name.children.first == nil
21
+ if not name
22
+ put 'class'
23
+ elsif name.type == :const and name.children.first == nil
22
24
  put 'class '
23
25
  parse name
24
26
  put '$' if proxied
@@ -38,7 +40,8 @@ module Ruby2JS
38
40
  begin
39
41
  class_name, @class_name = @class_name, name
40
42
  class_parent, @class_parent = @class_parent, inheritance
41
- @rbstack.push({})
43
+ @rbstack.push(@namespace.getOwnProps)
44
+ @rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
42
45
  constructor = []
43
46
  index = 0
44
47
 
@@ -46,10 +49,17 @@ module Ruby2JS
46
49
  body.each do |m|
47
50
  if m.type == :def
48
51
  prop = m.children.first
49
- if prop == :initialize
52
+ if prop == :initialize and !@rbstack.last[:initialize]
50
53
  constructor = m.children[2..-1]
51
- elsif not prop.to_s.end_with? '='
52
- @rbstack.last[prop] = s(:self)
54
+ elsif prop.to_s.end_with? '='
55
+ @rbstack.last[prop.to_s[0..-2].to_sym] = s(:autobind, s(:self))
56
+ else
57
+ @rbstack.last[prop] = m.is_method? ? s(:autobind, s(:self)) : s(:self)
58
+ end
59
+ elsif m.type == :send and m.children[0..1] == [nil, :async]
60
+ if m.children[2].type == :def
61
+ prop = m.children[2].children.first
62
+ @rbstack.last[prop] = s(:autobind, s(:self))
53
63
  end
54
64
  end
55
65
  end
@@ -146,10 +156,10 @@ module Ruby2JS
146
156
  end
147
157
  end
148
158
 
149
- if m.type == :def || m.type == :async
159
+ if m.type == :def || m.type == :defm || m.type == :async
150
160
  @prop = m.children.first
151
161
 
152
- if @prop == :initialize
162
+ if @prop == :initialize and !@rbstack.last[:initialize]
153
163
  @prop = :constructor
154
164
 
155
165
  if constructor == [] or constructor == [(:super)]
@@ -234,13 +244,27 @@ module Ruby2JS
234
244
  else
235
245
  if m.children[1] == :include
236
246
  m = m.updated(:begin, m.children[2..-1].map {|mname|
237
- s(:send, s(:const, nil, :Object), :assign,
238
- s(:attr, name, :prototype), mname)})
247
+ @namespace.defineProps @namespace.find(mname)
248
+ s(:assign, s(:attr, name, :prototype), mname)
249
+ })
239
250
  end
240
251
 
241
252
  skipped = true
242
253
  end
243
254
 
255
+ elsif es2022 and \
256
+ m.type == :send and m.children.first.type == :self and \
257
+ m.children[1].to_s.end_with? '='
258
+
259
+ put 'static '
260
+ parse m.updated(:lvasgn, [m.children[1].to_s.sub('=', ''),
261
+ m.children[2]])
262
+
263
+ elsif m.type == :defineProps
264
+ skipped = true
265
+ @namespace.defineProps m.children.first
266
+ @rbstack.last.merge! m.children.first
267
+
244
268
  else
245
269
  if m.type == :cvasgn and !underscored_private
246
270
  put 'static #$'; put m.children[0].to_s[2..-1]; put ' = '
@@ -263,7 +287,7 @@ module Ruby2JS
263
287
  end
264
288
 
265
289
  if skipped
266
- post << [m, comments] if skipped
290
+ post << [m, comments] unless m.type == :defineProps
267
291
  else
268
292
  comments.reverse.each {|comment| insert location, comment}
269
293
  end
@@ -300,6 +324,10 @@ module Ruby2JS
300
324
  else
301
325
  parse m.updated(:send, [@class_name, *m.children[1..-1]])
302
326
  end
327
+ elsif m.type == :block and m.children.first.children.first == nil
328
+ # class method calls passing a block
329
+ parse s(:block, s(:send, name, *m.children.first.children[1..-1]),
330
+ *m.children[1..-1])
303
331
  else
304
332
  parse m, :statement
305
333
  end
@@ -345,7 +373,7 @@ module Ruby2JS
345
373
  ensure
346
374
  @class_name = class_name
347
375
  @class_parent = class_parent
348
- @rbstack.pop
376
+ @namespace.defineProps @rbstack.pop
349
377
  end
350
378
  end
351
379
  end
@@ -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
@@ -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
@@ -73,7 +73,7 @@ module Ruby2JS
73
73
  if right.type == :hash
74
74
  right.children.each do |pair|
75
75
  next unless Parser::AST::Node === pair.children.last
76
- if [:block, :def, :async].include? pair.children.last.type
76
+ if %i[block def defm async].include? pair.children.last.type
77
77
  if @comments[pair.children.last]
78
78
  (puts ''; singleton = false) if singleton
79
79
  comments(pair.children.last).each do |comment|
@@ -120,22 +120,26 @@ module Ruby2JS
120
120
 
121
121
  if \
122
122
  anonfn and
123
- left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
123
+ left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
124
124
  then
125
125
  @prop = left.children.first
126
126
  parse right, :method
127
127
  elsif \
128
- es2015 and left.type == :sym and right.type == :lvar and
129
- left.children == right.children
128
+ es2015 and left.type == :sym and (right.type == :lvar or
129
+ (right.type == :send and right.children.first == nil)) and
130
+ left.children.last == right.children.last
130
131
  then
131
132
  parse right
133
+ elsif right.type == :defm and %i[sym str].include? left.type and es2015
134
+ @prop = left.children.first.to_s
135
+ parse right
132
136
  else
133
137
  if not [:str, :sym].include? left.type and es2015
134
138
  put '['
135
139
  parse left
136
140
  put ']'
137
141
  elsif \
138
- left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\Z/
142
+ left.children.first.to_s =~ /\A[a-zA-Z_$][a-zA-Z_$0-9]*\z/
139
143
  then
140
144
  put left.children.first
141
145
  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
@@ -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
@@ -20,8 +20,8 @@ module Ruby2JS
20
20
  def number_format(number)
21
21
  return number.to_s unless es2021
22
22
  parts = number.to_s.split('.')
23
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1_")
24
- parts[1].gsub!(/(\d\d\d)(?=\d)/, "\\1_") if parts[1]
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
25
  parts.join('.')
26
26
  end
27
27
  end
@@ -4,10 +4,22 @@ 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)
7
14
 
8
- handle :module do |name, *body|
9
15
  if body == [nil]
10
- parse @ast.updated(:casgn, [*name.children, s(:hash)])
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
11
23
  return
12
24
  end
13
25
 
@@ -15,8 +27,20 @@ module Ruby2JS
15
27
  body = body.first.children
16
28
  end
17
29
 
18
- if body.length > 0 and body.all? {|child| child.type == :def}
19
- 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
20
44
  return
21
45
  end
22
46
 
@@ -47,6 +71,8 @@ module Ruby2JS
47
71
  symbols << node.children.first
48
72
  elsif node.type == :class and node.children.first.children.first == nil
49
73
  symbols << node.children.first.children.last
74
+ elsif node.type == :module
75
+ symbols << node.children.first.children.last
50
76
  end
51
77
  end
52
78
 
@@ -55,11 +81,17 @@ module Ruby2JS
55
81
 
56
82
  body = s(:send, s(:block, s(:send, nil, :proc), s(:args),
57
83
  s(:begin, *body)), :[])
58
- 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
59
89
  parse s(:lvasgn, name.children.last, body)
60
90
  else
61
91
  parse s(:send, name.children.first, "#{name.children.last}=", body)
62
92
  end
93
+
94
+ @namespace.leave
63
95
  end
64
96
  end
65
97
  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])
@@ -41,6 +41,20 @@ module Ruby2JS
41
41
  # strip '!' and '?' decorations
42
42
  method = method.to_s[0..-2] if method =~ /\w[!?]$/
43
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
+
44
58
  # three ways to define anonymous functions
45
59
  if method == :new and receiver and receiver.children == [nil, :Proc]
46
60
  return parse args.first, @state
@@ -118,8 +132,13 @@ module Ruby2JS
118
132
 
119
133
  # resolve anonymous receivers against rbstack
120
134
  receiver ||= @rbstack.map {|rb| rb[method]}.compact.last
135
+ autobind = nil
121
136
 
122
137
  if receiver
138
+ if receiver.type == :autobind
139
+ autobind = receiver = receiver.children.first
140
+ end
141
+
123
142
  group_receiver = receiver.type == :send &&
124
143
  op_index < operator_index( receiver.children[1] ) if receiver
125
144
  group_receiver ||= GROUP_OPERATORS.include? receiver.type
@@ -176,9 +195,15 @@ module Ruby2JS
176
195
  receiver.type == :send and
177
196
  receiver.children[1] == :+@ and
178
197
  Parser::AST::Node === receiver.children[0] and
179
- receiver.children[0].type == :class
198
+ %i(class module).include? receiver.children[0].type
180
199
  then
181
- parse receiver.children[0].updated(:class_extend)
200
+ if receiver.children[0].type == :class
201
+ parse receiver.children[0].updated(:class_extend)
202
+ else
203
+ mod = receiver.children[0]
204
+ parse s(:assign, mod.children[0],
205
+ mod.updated(nil, [nil, *mod.children[1..-1]]))
206
+ end
182
207
  else
183
208
  put method.to_s[0]; parse receiver
184
209
  end
@@ -281,6 +306,12 @@ module Ruby2JS
281
306
  else
282
307
  put 'await ' if @ast.type == :await
283
308
 
309
+ if method == :bind and receiver&.type == :send
310
+ if receiver.children.length == 2 and receiver.children.first == nil
311
+ receiver = receiver.updated(:attr) # prevent autobind
312
+ end
313
+ end
314
+
284
315
  if not ast.is_method? and ast.type != :send!
285
316
  if receiver
286
317
  (group_receiver ? group(receiver) : parse(receiver))
@@ -303,6 +334,14 @@ module Ruby2JS
303
334
  compact { puts "("; parse_all(*args, join: ",#@ws"); sput ')' }
304
335
  end
305
336
  end
337
+
338
+ if autobind and not ast.is_method? and ast.type != :attr
339
+ if @state == :statement
340
+ put '()'
341
+ else
342
+ put '.bind('; parse(autobind); put ')'
343
+ end
344
+ end
306
345
  end
307
346
  end
308
347