ruby2js 3.6.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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