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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11ab4e0003f0ff4f5821c00d5e282dead1201f97d4a2cc9ffb5c1188687f6079
4
- data.tar.gz: fe572bef6b5ee55d46d72bb71dfcca5793fff8dd29a44243da637a7b91264bf7
3
+ metadata.gz: 064d9dc5d65f6e2456e91d951e2f4311b776221422a77d59a8cfd466ceb5056d
4
+ data.tar.gz: ffc274a55ceb9a1280f776c00da8c296dc10672150174135931547cd056a3520
5
5
  SHA512:
6
- metadata.gz: f865febd41bda90440a03b5ccdb7ac921eb794701c61d120b28a850d03001ee95ae0403bd50123d96c0f85b5e3a0e672066fcde7da68216c28f6f5895b2f9398
7
- data.tar.gz: cf4811376c6daf78e951f688b6b2ee78e1b627135ed365e7197a6a318601b8b6313d2c4123031fd395cf5342707914e56bc29d946e15dc8f1dc7d53d35b053c6
6
+ metadata.gz: 8fe9775803716fce25697adaf59a8afb57f4de499052a66847f18dfdd59c1f73b52d77be473adf762a57693a0896a6c90ed275a2b9152b5b6b548ca454e6d9ae
7
+ data.tar.gz: aa901eab47bba051a69a0ee132b69e7754aa5da49ff2e7e22560303a4c0fd02cdb727d4297c13098f3a6f82cf20788f4657c7ec21d145e66533875933d1a5154
data/README.md CHANGED
@@ -13,7 +13,7 @@ Documentation
13
13
 
14
14
  * Visit **[ruby2js.com](https://www.ruby2js.com)** for detailed setup instructions and API reference.
15
15
 
16
- * [Try Ruby2JS online](https://whimsy.apache.org/ruby2js)
16
+ * [Try Ruby2JS online](https://ruby2js.com/demo)
17
17
 
18
18
 
19
19
  Synopsis
@@ -39,12 +39,6 @@ Host variable substitution:
39
39
  puts Ruby2JS.convert("@name", ivars: {:@name => "Joe"})
40
40
  ```
41
41
 
42
- Host expression evalution -- potentially unsafe, use only if you trust
43
- the source being converted::
44
- ```ruby
45
- i = 7; puts Ruby2JS.convert("i = `i`", binding: binding)
46
- ```
47
-
48
42
  Enable ES2015 support:
49
43
 
50
44
  ```ruby
data/lib/ruby2js.rb CHANGED
@@ -10,9 +10,15 @@ end
10
10
 
11
11
  require 'ruby2js/converter'
12
12
  require 'ruby2js/filter'
13
+ require 'ruby2js/namespace'
13
14
 
14
15
  module Ruby2JS
15
16
  class SyntaxError < RuntimeError
17
+ attr_reader :diagnostic
18
+ def initialize(message, diagnostic=nil)
19
+ super(message)
20
+ @diagnostic = diagnostic
21
+ end
16
22
  end
17
23
 
18
24
  @@eslevel_default = 2009 # ecmascript 5
@@ -61,7 +67,7 @@ module Ruby2JS
61
67
  include Ruby2JS::Filter
62
68
  BINARY_OPERATORS = Converter::OPERATORS[2..-1].flatten
63
69
 
64
- attr_accessor :prepend_list, :disable_autoimports
70
+ attr_accessor :prepend_list, :disable_autoimports, :namespace
65
71
 
66
72
  def initialize(comments)
67
73
  @comments = comments
@@ -79,8 +85,8 @@ module Ruby2JS
79
85
  def options=(options)
80
86
  @options = options
81
87
 
82
- @included = @@included
83
- @excluded = @@excluded
88
+ @included = Filter.included_methods
89
+ @excluded = Filter.excluded_methods
84
90
 
85
91
  include_all if options[:include_all]
86
92
  include_only(options[:include_only]) if options[:include_only]
@@ -116,6 +122,10 @@ module Ruby2JS
116
122
  @options[:eslevel] >= 2021
117
123
  end
118
124
 
125
+ def es2022
126
+ @options[:eslevel] >= 2022
127
+ end
128
+
119
129
  def process(node)
120
130
  ast, @ast = @ast, node
121
131
  replacement = super
@@ -130,6 +140,7 @@ module Ruby2JS
130
140
  end
131
141
 
132
142
  # handle all of the 'invented/synthetic' ast types
143
+ def on_assign(node); end
133
144
  def on_async(node); on_def(node); end
134
145
  def on_asyncs(node); on_defs(node); end
135
146
  def on_attr(node); on_send(node); end
@@ -137,18 +148,23 @@ module Ruby2JS
137
148
  def on_await(node); on_send(node); end
138
149
  def on_call(node); on_send(node); end
139
150
  def on_class_extend(node); on_send(node); end
151
+ def on_class_hash(node); on_class(node); end
140
152
  def on_class_module(node); on_send(node); end
141
153
  def on_constructor(node); on_def(node); end
154
+ def on_deff(node); on_def(node); end
142
155
  def on_defm(node); on_defs(node); end
143
156
  def on_defp(node); on_defs(node); end
144
157
  def on_for_of(node); on_for(node); end
145
158
  def on_in?(node); on_send(node); end
146
159
  def on_method(node); on_send(node); end
160
+ def on_module_hash(node); on_module(node); end
147
161
  def on_prop(node); on_array(node); end
148
162
  def on_prototype(node); on_begin(node); end
149
163
  def on_send!(node); on_send(node); end
150
164
  def on_sendw(node); on_send(node); end
151
165
  def on_undefined?(node); on_defined?(node); end
166
+ def on_defineProps(node); end
167
+ def on_hide(node); on_begin(node); end
152
168
  def on_nil(node); end
153
169
  def on_xnode(node); end
154
170
  def on_export(node); end
@@ -178,6 +194,7 @@ module Ruby2JS
178
194
  end
179
195
 
180
196
  def self.convert(source, options={})
197
+ options = options.dup
181
198
  options[:eslevel] ||= @@eslevel_default
182
199
  options[:strict] = @@strict_default if options[:strict] == nil
183
200
  options[:module] ||= @@module_default || :esm
@@ -197,6 +214,8 @@ module Ruby2JS
197
214
  comments = ast ? Parser::Source::Comment.associate(ast, comments) : {}
198
215
  end
199
216
 
217
+ namespace = Namespace.new
218
+
200
219
  filters = (options[:filters] || Filter::DEFAULTS)
201
220
 
202
221
  unless filters.empty?
@@ -211,6 +230,7 @@ module Ruby2JS
211
230
  filter = filter.new(comments)
212
231
 
213
232
  filter.options = options
233
+ filter.namespace = namespace
214
234
  ast = filter.process(ast)
215
235
 
216
236
  unless filter.prepend_list.empty?
@@ -229,7 +249,10 @@ module Ruby2JS
229
249
  ruby2js.comparison = options[:comparison] || :equality
230
250
  ruby2js.or = options[:or] || :logical
231
251
  ruby2js.module_type = options[:module] || :esm
232
- ruby2js.underscored_private = (options[:eslevel] < 2020) || options[:underscored_private]
252
+ ruby2js.underscored_private = (options[:eslevel] < 2022) || options[:underscored_private]
253
+
254
+ ruby2js.namespace = namespace
255
+
233
256
  if ruby2js.binding and not ruby2js.ivars
234
257
  ruby2js.ivars = ruby2js.binding.eval \
235
258
  'Hash[instance_variables.map {|var| [var, instance_variable_get(var)]}]'
@@ -256,16 +279,16 @@ module Ruby2JS
256
279
  buffer = Parser::Source::Buffer.new(file, line)
257
280
  buffer.source = source.encode('utf-8')
258
281
  parser = Parser::CurrentRuby.new
259
- parser.builder.emit_file_line_as_literals=false
260
- ast, comments = parser.parse_with_comments(buffer)
261
- Parser::CurrentRuby.parse(source.encode('utf-8')) unless ast
262
- [ast, comments]
282
+ parser.diagnostics.all_errors_are_fatal = true
283
+ parser.diagnostics.consumer = lambda {|diagnostic| nil}
284
+ parser.builder.emit_file_line_as_literals = false
285
+ parser.parse_with_comments(buffer)
263
286
  rescue Parser::SyntaxError => e
264
287
  split = source[0..e.diagnostic.location.begin_pos].split("\n")
265
288
  line, col = split.length, split.last.length
266
289
  message = "line #{line}, column #{col}: #{e.diagnostic.message}"
267
290
  message += "\n in file #{file}" if file
268
- raise Ruby2JS::SyntaxError.new(message)
291
+ raise Ruby2JS::SyntaxError.new(message, e.diagnostic)
269
292
  end
270
293
 
271
294
  def self.find_block(ast, line)
@@ -14,7 +14,7 @@ module Ruby2JS
14
14
 
15
15
  class Converter < Serializer
16
16
  attr_accessor :ast
17
-
17
+
18
18
  LOGICAL = :and, :not, :or
19
19
  OPERATORS = [:[], :[]=], [:not, :!], [:**], [:*, :/, :%], [:+, :-],
20
20
  [:>>, :<<], [:&], [:^, :|], [:<=, :<, :>, :>=], [:==, :!=, :===, :"!=="],
@@ -34,7 +34,7 @@ module Ruby2JS
34
34
 
35
35
  VASGN = [:cvasgn, :ivasgn, :gvasgn, :lvasgn]
36
36
 
37
- attr_accessor :binding, :ivars
37
+ attr_accessor :binding, :ivars, :namespace
38
38
 
39
39
  def initialize( ast, comments, vars = {} )
40
40
  super()
@@ -160,6 +160,10 @@ module Ruby2JS
160
160
  @eslevel >= 2021
161
161
  end
162
162
 
163
+ def es2022
164
+ @eslevel >= 2022
165
+ end
166
+
163
167
  @@handlers = []
164
168
  def self.handle(*types, &block)
165
169
  types.each do |type|
@@ -295,6 +299,7 @@ end
295
299
  require 'ruby2js/converter/arg'
296
300
  require 'ruby2js/converter/args'
297
301
  require 'ruby2js/converter/array'
302
+ require 'ruby2js/converter/assign'
298
303
  require 'ruby2js/converter/begin'
299
304
  require 'ruby2js/converter/block'
300
305
  require 'ruby2js/converter/blockpass'
@@ -314,6 +319,7 @@ require 'ruby2js/converter/dstr'
314
319
  require 'ruby2js/converter/fileline'
315
320
  require 'ruby2js/converter/for'
316
321
  require 'ruby2js/converter/hash'
322
+ require 'ruby2js/converter/hide'
317
323
  require 'ruby2js/converter/if'
318
324
  require 'ruby2js/converter/in'
319
325
  require 'ruby2js/converter/import'
@@ -0,0 +1,159 @@
1
+ # Kinda like Object.assign, except it handles properties
2
+ #
3
+ # Note: Object.defineProperties, Object.getOwnPropertyNames, etc. technically
4
+ # were not part of ES5, but were implemented by IE prior to ES6, and are
5
+ # the only way to implement getters and setters.
6
+
7
+ module Ruby2JS
8
+ class Converter
9
+
10
+ # (assign
11
+ # target
12
+ # (hash)
13
+ # ...
14
+
15
+ handle :assign do |target, *args|
16
+ collapsible = false
17
+
18
+ nonprop = proc do |node|
19
+ next false unless node.is_a? Parser::AST::Node
20
+ next false if node.type == :pair and node.children.first.type == :prop and es2015
21
+ next true unless node.type == :def
22
+ next false if node.children.first.to_s.end_with? '='
23
+ node.is_method?
24
+ end
25
+
26
+ collapsible = true if args.length == 1 and args.first.type == :hash and
27
+ args.first.children.length == 1
28
+
29
+ collapsible = true if args.length == 1 and args.first.type == :class_module and
30
+ args.first.children.length == 3 and nonprop[args.first.children.last]
31
+
32
+ if es2015 and not collapsible and args.all? {|arg|
33
+ case arg.type
34
+ when :pair, :hash, :class_module
35
+ arg.children.all? {|child| nonprop[child]}
36
+ when :const
37
+ false
38
+ else
39
+ true
40
+ end
41
+ }
42
+ parse s(:send, s(:const, nil, :Object), :assign, target, *args)
43
+ else
44
+
45
+ if target == s(:hash)
46
+ copy = [s(:gvasgn, :$$, target)]
47
+ target = s(:gvar, :$$)
48
+ shadow = [s(:shadowarg, :$$)]
49
+ elsif collapsible or es2015 or
50
+ (%i(send const).include? target.type and
51
+ target.children.length == 2 and target.children[0] == nil)
52
+ then
53
+ copy = []
54
+ shadow = []
55
+ else
56
+ copy = [s(:gvasgn, :$0, target)]
57
+ target = s(:gvar, :$0)
58
+ shadow = [s(:shadowarg, :$0)]
59
+ end
60
+
61
+ body = [*copy,
62
+ *args.map {|modname|
63
+ if modname.type == :hash and
64
+ modname.children.all? {|pair| pair.children.first.type == :prop}
65
+
66
+ if modname.children.length == 1
67
+ pair = modname.children.first
68
+ s(:send, s(:const, nil, :Object), :defineProperty, target,
69
+ s(:sym, pair.children.first.children.last),
70
+ s(:hash, *pair.children.last.map {|name, value| s(:pair,
71
+ s(:sym, name), value)}))
72
+ else
73
+ pair = modname.children.first
74
+ s(:send, s(:const, nil, :Object), :defineProperties, target,
75
+ s(:hash, *modname.children.map {|pair| s(:pair,
76
+ s(:sym, pair.children.first.children.last),
77
+ s(:hash, *pair.children.last.map {|name, value| s(:pair,
78
+ s(:sym, name), value)})
79
+ )}))
80
+ end
81
+
82
+ elsif modname.type == :hash and
83
+ modname.children.all? {|child| nonprop[child]}
84
+
85
+ s(:begin, *modname.children.map {|pair|
86
+ if pair.children.first.type == :prop
87
+ s(:send, s(:const, nil, :Object), :defineProperty, target,
88
+ s(:sym, pair.children.first.children.last),
89
+ s(:hash, *pair.children.last.map {|name, value| s(:pair,
90
+ s(:sym, name), value)}))
91
+ else
92
+ s(:send, target, :[]=, *pair.children)
93
+ end
94
+ })
95
+
96
+ elsif modname.type == :class_module and
97
+ modname.children[2..-1].all? {|child| nonprop[child]}
98
+
99
+ s(:begin, *modname.children[2..-1].map {|pair|
100
+ s(:send, target, :[]=, s(:sym, pair.children.first),
101
+ pair.updated(:defm, [nil, *pair.children[1..-1]]))
102
+ })
103
+
104
+ elsif modname.type == :lvar and not es2015
105
+ s(:for, s(:lvasgn, :$_), modname,
106
+ s(:send, target, :[]=,
107
+ s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
108
+
109
+ else
110
+ if es2017
111
+ s(:send, s(:const, nil, :Object), :defineProperties, target,
112
+ s(:send, s(:const, nil, :Object), :getOwnPropertyDescriptors, modname))
113
+ else
114
+ if modname.type == :lvar or (%i(send const).include? modname.type and
115
+ modname.children.length == 2 and modname.children[0] == nil)
116
+
117
+ object = modname
118
+ else
119
+ shadow += [s(:shadowarg, :$1)]
120
+ object = s(:gvar, :$1)
121
+ end
122
+
123
+ copy = s(:send,
124
+ s(:const, nil, :Object), :defineProperties, target,
125
+ s(:send,
126
+ s(:send, s(:const, nil, :Object), :getOwnPropertyNames, object),
127
+ :reduce,
128
+ s(:block,
129
+ s(:send, nil, :lambda),
130
+ s(:args, s(:arg, :$2), s(:arg, :$3)),
131
+ s(:begin,
132
+ s(:send,
133
+ s(:lvar, :$2), :[]=, s(:lvar, :$3),
134
+ s(:send, s(:const, nil, :Object), :getOwnPropertyDescriptor,
135
+ object, s(:lvar, :$3))),
136
+ s(:return, s(:lvar, :$2)))),
137
+ s(:hash)))
138
+
139
+
140
+ if object.type == :gvar
141
+ s(:begin, s(:gvasgn, object.children.last, modname), copy)
142
+ else
143
+ copy
144
+ end
145
+ end
146
+ end
147
+ }]
148
+
149
+ if @state == :statement and shadow.empty?
150
+ parse s(:begin, *body)
151
+ else
152
+ body.push s(:return, target) if @state == :expression
153
+ parse s(:send, s(:block, s(:send, nil, :lambda), s(:args, *shadow),
154
+ s(:begin, *body)), :[])
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -9,6 +9,11 @@ module Ruby2JS
9
9
  state = @state
10
10
  props = false
11
11
 
12
+ if state == :expression and statements.empty?
13
+ puts 'null'
14
+ return
15
+ end
16
+
12
17
  statements.map! do |statement|
13
18
  case statement and statement.type
14
19
  when :defs, :defp
@@ -32,9 +37,9 @@ module Ruby2JS
32
37
  end
33
38
 
34
39
  def combine_properties(body)
35
- for i in 0...body.length-1
40
+ (0...body.length-1).each do |i|
36
41
  next unless body[i] and body[i].type == :prop
37
- for j in i+1...body.length
42
+ (i+1...body.length).each do |j|
38
43
  break unless body[j] and body[j].type == :prop
39
44
 
40
45
  if body[i].children[0] == body[j].children[0]
@@ -10,10 +10,15 @@ module Ruby2JS
10
10
 
11
11
  handle :case do |expr, *whens, other|
12
12
  begin
13
+ if @state == :expression
14
+ parse s(:kwbegin, @ast), @state
15
+ return
16
+ end
17
+
13
18
  inner, @inner = @inner, @ast
14
19
 
15
20
  has_range = whens.any? do |node|
16
- node.children.any? {|child| [:irange, :erange].include? child.type}
21
+ node.children.any? {|child| [:irange, :erange].include? child&.type}
17
22
  end
18
23
 
19
24
  if has_range
@@ -47,7 +52,7 @@ module Ruby2JS
47
52
 
48
53
  parse code, :statement
49
54
  last = code
50
- while last.type == :begin
55
+ while last&.type == :begin
51
56
  last = last.children.last
52
57
  end
53
58
 
@@ -11,13 +11,27 @@ module Ruby2JS
11
11
  # NOTE: class_extend is not generated by the parser, but instead produced
12
12
  # when ++class is encountered; it signals that this construct is
13
13
  # meant to extend an already existing JavaScrpt class.
14
+ #
15
+ # class_hash is an anonymous class as a value in a hash; the
16
+ # name has already been output so should be ignored other than
17
+ # in determining the namespace.
18
+ #
19
+ # class_module is a module that to be re-processed by this handler
20
+ # given the similarity between the two structures.
14
21
 
15
- handle :class, :class_extend, :class_module do |name, inheritance, *body|
16
- if @ast.type != :class
22
+ handle :class, :class_hash, :class_extend, :class_module do |name, inheritance, *body|
23
+ extend = @namespace.enter(name) unless @ast.type == :class_module
24
+
25
+ if !%i(class class_hash).include?(@ast.type) or extend
17
26
  init = nil
18
27
  else
19
- if es2015
20
- parse @ast.updated(:class2)
28
+ if es2015 and not extend
29
+ if @ast.type == :class_hash
30
+ parse @ast.updated(:class2, [nil, *@ast.children[1..-1]])
31
+ else
32
+ parse @ast.updated(:class2)
33
+ end
34
+ @namespace.leave unless @ast.type == :class_module
21
35
  return
22
36
  end
23
37
 
@@ -35,7 +49,7 @@ module Ruby2JS
35
49
  end
36
50
 
37
51
  body.compact!
38
- visible = {}
52
+ visible = @namespace.getOwnProps
39
53
  body.map! do |m|
40
54
  if \
41
55
  @ast.type == :class_module and m.type == :defs and
@@ -45,7 +59,7 @@ module Ruby2JS
45
59
  end
46
60
 
47
61
  node = if m.type == :def
48
- if m.children.first == :initialize
62
+ if m.children.first == :initialize and !visible[:initialize]
49
63
  # constructor: remove from body and overwrite init function
50
64
  init = m
51
65
  nil
@@ -54,17 +68,20 @@ module Ruby2JS
54
68
  sym = :"#{m.children.first.to_s[0..-2]}"
55
69
  s(:prop, s(:attr, name, :prototype), sym =>
56
70
  {enumerable: s(:true), configurable: s(:true),
57
- set: s(:block, s(:send, nil, :proc), *m.children[1..-1])})
71
+ set: s(:defm, nil, *m.children[1..-1])})
58
72
  else
59
- visible[m.children[0]] = s(:self)
60
73
 
61
74
  if not m.is_method?
75
+ visible[m.children[0]] = s(:self)
76
+
62
77
  # property getter
63
78
  s(:prop, s(:attr, name, :prototype), m.children.first =>
64
79
  {enumerable: s(:true), configurable: s(:true),
65
- get: s(:block, s(:send, nil, :proc), m.children[1],
80
+ get: s(:defm, nil, m.children[1],
66
81
  m.updated(:autoreturn, m.children[2..-1]))})
67
82
  else
83
+ visible[m.children[0]] = s(:autobind, s(:self))
84
+
68
85
  # method: add to prototype
69
86
  s(:method, s(:attr, name, :prototype),
70
87
  :"#{m.children[0].to_s.chomp('!')}=",
@@ -90,7 +107,7 @@ module Ruby2JS
90
107
  else
91
108
  # class method definition: add to prototype
92
109
  s(:prototype, s(:send, name, "#{m.children[1]}=",
93
- s(:block, s(:send, nil, :proc), *m.children[2..-1])))
110
+ s(:defm, nil, *m.children[2..-1])))
94
111
  end
95
112
 
96
113
  elsif m.type == :send and m.children.first == nil
@@ -125,9 +142,10 @@ module Ruby2JS
125
142
  elsif m.children[1] == :include
126
143
  s(:send, s(:block, s(:send, nil, :lambda), s(:args),
127
144
  s(:begin, *m.children[2..-1].map {|modname|
128
- s(:for, s(:lvasgn, :$_), modname,
129
- s(:send, s(:attr, name, :prototype), :[]=,
130
- s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
145
+ @namespace.defineProps @namespace.find(modname)
146
+ s(:for, s(:lvasgn, :$_), modname,
147
+ s(:send, s(:attr, name, :prototype), :[]=,
148
+ s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
131
149
  })), :[])
132
150
  elsif [:private, :protected, :public].include? m.children[1]
133
151
  raise Error.new("class #{m.children[1]} is not supported", @ast)
@@ -160,17 +178,23 @@ module Ruby2JS
160
178
  s(:send, s(:attr, name, :prototype),
161
179
  "#{m.children[0].children.first}=",
162
180
  s(:attr, s(:attr, name, :prototype), m.children[1].children.first))
163
- elsif m.type == :class
181
+ elsif m.type == :class or m.type == :module
164
182
  innerclass_name = m.children.first
165
183
  if innerclass_name.children.first
166
184
  innerclass_name = innerclass_name.updated(nil,
167
- [s(:attr, innerclass_name.children[0], name),
185
+ [s(:attr, name, innerclass_name.children[0].children.last),
168
186
  innerclass_name.children[1]])
169
187
  else
170
188
  innerclass_name = innerclass_name.updated(nil,
171
189
  [name, innerclass_name.children[1]])
172
190
  end
173
191
  m.updated(nil, [innerclass_name, *m.children[1..-1]])
192
+ elsif @ast.type == :class_module
193
+ m
194
+ elsif m.type == :defineProps
195
+ @namespace.defineProps m.children.first
196
+ visible.merge! m.children.first
197
+ nil
174
198
  else
175
199
  raise Error.new("class #{ m.type } not supported", @ast)
176
200
  end
@@ -194,7 +218,7 @@ module Ruby2JS
194
218
  # merge property definitions
195
219
  combine_properties(body)
196
220
 
197
- if inheritance
221
+ if inheritance and (@ast.type != :class_extend and !extend)
198
222
  body.unshift s(:send, name, :prototype=,
199
223
  s(:send, s(:const, nil, :Object), :create,
200
224
  s(:attr, inheritance, :prototype))),
@@ -206,10 +230,14 @@ module Ruby2JS
206
230
  methods = 0
207
231
  start = 0
208
232
  body.each do |node|
209
- if [:method, :prop].include? node.type and
233
+ if (node.type == :method or (node.type == :prop and es2015)) and
210
234
  node.children[0].type == :attr and
211
235
  node.children[0].children[1] == :prototype
212
236
  methods += 1
237
+ elsif node.type == :class and @ast.type == :class_module and es2015
238
+ methods += 1 if node.children.first.children.first == name
239
+ elsif node.type == :module and @ast.type == :class_module
240
+ methods += 1 if node.children.first.children.first == name
213
241
  elsif methods == 0
214
242
  start += 1
215
243
  else
@@ -219,14 +247,22 @@ module Ruby2JS
219
247
 
220
248
  # collapse sequence to a single assignment
221
249
  if \
222
- @ast.type != :class_extend and
223
- (methods > 1 or (methods == 1 and body[start].type == :prop))
250
+ @ast.type == :class_module or methods > 1 or
251
+ body[start]&.type == :prop
224
252
  then
225
253
  pairs = body[start...start+methods].map do |node|
226
254
  if node.type == :method
227
255
  replacement = node.updated(:pair, [
228
256
  s(:str, node.children[1].to_s.chomp('=')),
229
257
  node.children[2]])
258
+ elsif node.type == :class and node.children.first.children.first == name
259
+ sym = node.children.first.children.last
260
+ replacement = s(:pair, s(:sym, sym),
261
+ s(:class_hash, s(:const, nil, sym), nil, node.children.last))
262
+ elsif node.type == :module and node.children.first.children.first == name
263
+ sym = node.children.first.children.last
264
+ replacement = s(:pair, s(:sym, sym),
265
+ s(:module_hash, s(:const, nil, sym), node.children.last))
230
266
  else
231
267
  replacement = node.children[1].map do |prop, descriptor|
232
268
  node.updated(:pair, [s(:prop, prop), descriptor])
@@ -244,12 +280,30 @@ module Ruby2JS
244
280
  end
245
281
 
246
282
  if @ast.type == :class_module
283
+ start = 0 if methods == 0
284
+ if name
285
+ body[start...start+methods] =
286
+ s(:casgn, *name.children, s(:hash, *pairs.flatten))
287
+ else
288
+ body[start...start+methods] = s(:hash, *pairs.flatten)
289
+ end
290
+ elsif @ast.type == :class_extend or extend
247
291
  body[start...start+methods] =
248
- s(:casgn, *name.children, s(:hash, *pairs.flatten))
292
+ s(:assign, body[start].children.first, s(:hash, *pairs.flatten))
249
293
  else
250
294
  body[start...start+methods] =
251
295
  s(:send, name, :prototype=, s(:hash, *pairs.flatten))
252
296
  end
297
+
298
+ elsif (@ast.type == :class_extend or extend) and methods > 1
299
+
300
+ pairs = body[start...start+methods].map do |node|
301
+ node.updated(:pair, [
302
+ s(:sym, node.children[1].to_s[0..-2]), node.children[2]])
303
+ end
304
+
305
+ body[start...start+methods] =
306
+ s(:assign, body[start].children.first, s(:hash, *pairs))
253
307
  end
254
308
  end
255
309
 
@@ -258,7 +312,7 @@ module Ruby2JS
258
312
  constructor = init.updated(:constructor, [name, *init.children[1..-1]])
259
313
  @comments[constructor] = @comments[init] unless @comments[init].empty?
260
314
 
261
- if @ast.type == :class_extend
315
+ if @ast.type == :class_extend or extend
262
316
  if es2015
263
317
  constructor = s(:masgn, s(:mlhs,
264
318
  s(:attr, s(:casgn, *name.children, constructor), :prototype)),
@@ -285,6 +339,7 @@ module Ruby2JS
285
339
 
286
340
  # add locally visible interfaces to rbstack. See send.rb, const.rb
287
341
  @rbstack.push visible
342
+ @rbstack.last.merge!(@namespace.find(inheritance)) if inheritance
288
343
 
289
344
  parse s(:begin, *body.compact), :statement
290
345
  ensure
@@ -292,6 +347,7 @@ module Ruby2JS
292
347
  @class_name = class_name
293
348
  @class_parent = class_parent
294
349
  @rbstack.pop
350
+ @namespace.leave unless @ast.type == :class_module
295
351
  end
296
352
  end
297
353