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
@@ -8,6 +8,11 @@ module Ruby2JS
8
8
  # (int 1))
9
9
 
10
10
  handle :lvasgn, :gvasgn do |name, value=nil|
11
+ if @ast.type == :lvasgn and value
12
+ receiver = @rbstack.map {|rb| rb[name]}.compact.last
13
+ return parse s(:attr, receiver, "#{name}=", value) if receiver
14
+ end
15
+
11
16
  state = @state
12
17
  begin
13
18
  if value and value.type == :lvasgn and @state == :statement
@@ -0,0 +1,53 @@
1
+ # helper methods shared between MRI CGI/server and Opal implementations
2
+ require 'ruby2js'
3
+
4
+ module Ruby2JS
5
+ module Demo
6
+ def self.parse_autoimports(mappings)
7
+ autoimports = {}
8
+
9
+ mappings = mappings.gsub(/\s+|"|'/, '')
10
+
11
+ while mappings and not mappings.empty?
12
+ if mappings =~ /^(\w+):([^,]+)(,(.*))?$/
13
+ # symbol: module
14
+ autoimports[$1.to_sym] = $2
15
+ mappings = $4
16
+ elsif mappings =~ /^\[([\w,]+)\]:([^,]+)(,(.*))?$/
17
+ # [symbol, symbol]: module
18
+ mname, mappings = $2, $4
19
+ autoimports[$1.split(/,/).map(&:to_sym)] = mname
20
+ elsif mappings =~ /^(\w+)(,(.*))?$/
21
+ # symbol
22
+ autoimports[$1.to_sym] = $1
23
+ mappings = $3
24
+ elsif not mappings.empty?
25
+ $load_error = "unsupported autoimports mapping: #{mappings}"
26
+ mappings = ''
27
+ end
28
+ end
29
+
30
+ # if nothing is listed, provide a mapping for everything
31
+ autoimports = proc {|name| name.to_s} if autoimports.empty?
32
+
33
+ autoimports
34
+ end
35
+
36
+ def self.parse_defs(mappings)
37
+ defs = {}
38
+
39
+ mappings = mappings.gsub(/\s+|"|'/, '')
40
+
41
+ while mappings =~ /^(\w+):\[(:?@?\w+(,:?@?\w+)*)\](,(.*))?$/
42
+ mappings = $5
43
+ defs[$1.to_sym] = $2.gsub(':', '').split(',').map(&:to_sym)
44
+ end
45
+
46
+ if mappings and not mappings.empty?
47
+ $load_error = "unsupported defs: #{mappings}"
48
+ end
49
+
50
+ defs
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ require 'ruby2js'
2
+
3
+ if Ruby2JS.eslevel_default < 2022
4
+ Ruby2JS.eslevel_default = 2022
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'ruby2js/es2022'
2
+
3
+ Ruby2JS.strict_default = true
@@ -13,6 +13,14 @@ module Ruby2JS
13
13
  @@included = nil
14
14
  @@excluded = []
15
15
 
16
+ def self.included_methods
17
+ @@included&.dup
18
+ end
19
+
20
+ def self.excluded_methods
21
+ @@excluded&.dup
22
+ end
23
+
16
24
  # indicate that the specified methods are not to be processed
17
25
  def self.exclude(*methods)
18
26
  if @@included
@@ -52,7 +60,7 @@ module Ruby2JS
52
60
  not @included.include? method
53
61
  else
54
62
  return true if @exclude_methods.flatten.include? method
55
- @excluded.include? method
63
+ @excluded&.include? method
56
64
  end
57
65
  end
58
66
 
@@ -7,6 +7,7 @@ module Ruby2JS
7
7
 
8
8
  def on_send(node)
9
9
  target, method, *args = node.children
10
+ return super unless args.empty?
10
11
 
11
12
  if es2015 and method == :blank?
12
13
  create_or_update_import("blank$")
@@ -1,5 +1,7 @@
1
1
  require 'ruby2js'
2
2
 
3
+ Ruby2JS.module_default ||= :cjs
4
+
3
5
  module Ruby2JS
4
6
  module Filter
5
7
  module CJS
@@ -9,20 +9,21 @@ module Ruby2JS
9
9
 
10
10
  def options=(options)
11
11
  super
12
- @esm_autoexports = options[:autoexports] && !@disable_autoexports
12
+ @esm_autoexports = !@disable_autoexports && options[:autoexports]
13
13
  @esm_autoimports = options[:autoimports]
14
+ @esm_defs = options[:defs] || {}
14
15
  @esm_explicit_tokens = Set.new
15
16
  end
16
17
 
17
18
  def process(node)
18
19
  return super unless @esm_autoexports
19
- @esm_autoexports = false
20
20
 
21
21
  list = [node]
22
22
  while list.length == 1 and list.first.type == :begin
23
23
  list = list.first.children.dup
24
24
  end
25
25
 
26
+ replaced = []
26
27
  list.map! do |child|
27
28
  replacement = child
28
29
 
@@ -37,13 +38,27 @@ module Ruby2JS
37
38
  replacement = s(:export, child)
38
39
  end
39
40
 
40
- if replacement != child and @comments[child]
41
- @comments[replacement] = @comments[child]
41
+ if replacement != child
42
+ replaced << replacement
43
+ @comments[replacement] = @comments[child] if @comments[child]
42
44
  end
43
45
 
44
46
  replacement
45
47
  end
46
48
 
49
+ if replaced.length == 1 and @esm_autoexports == :default
50
+ list.map! do |child|
51
+ if child == replaced.first
52
+ replacement = s(:export, s(:send, nil, :default, *child.children))
53
+ @comments[replacement] = @comments[child] if @comments[child]
54
+ replacement
55
+ else
56
+ child
57
+ end
58
+ end
59
+ end
60
+
61
+ @esm_autoexports = false
47
62
  process s(:begin, *list)
48
63
  end
49
64
 
@@ -78,7 +93,7 @@ module Ruby2JS
78
93
  end
79
94
  end
80
95
 
81
- if args[0].type == :str
96
+ if args[0].type == :str and args.length == 1
82
97
  # import "file.css"
83
98
  # => import "file.css"
84
99
  s(:import, args[0].children[0])
@@ -106,15 +121,20 @@ module Ruby2JS
106
121
  # => import Stuff as * from "file.js"
107
122
  # import [ Some, Stuff ], from: "file.js"
108
123
  # => import { Some, Stuff } from "file.js"
109
- imports = if args[0].type == :const || args[0].type == :send
124
+ # import Some, [ More, Stuff ], from: "file.js"
125
+ # => import Some, { More, Stuff } from "file.js"
126
+ imports = []
127
+ if %i(const send str).include? args[0].type
110
128
  @esm_explicit_tokens << args[0].children.last
111
- process(args[0])
112
- else
129
+ imports << process(args.shift)
130
+ end
131
+
132
+ if args[0].type == :array
113
133
  args[0].children.each {|i| @esm_explicit_tokens << i.children.last}
114
- process_all(args[0].children)
134
+ imports << process_all(args.shift.children)
115
135
  end
116
136
 
117
- s(:import, args[1].children, imports) unless args[1].nil?
137
+ s(:import, args[0].children, *imports) unless args[0].nil?
118
138
  end
119
139
  elsif method == :export
120
140
  s(:export, *process_all(args))
@@ -129,6 +149,20 @@ module Ruby2JS
129
149
  def on_const(node)
130
150
  if node.children.first == nil and found_import = find_autoimport(node.children.last)
131
151
  prepend_list << s(:import, found_import[0], found_import[1])
152
+
153
+ values = @esm_defs[node.children.last]
154
+
155
+ if values
156
+ values = values.map {|value|
157
+ if value.to_s.start_with? "@"
158
+ [value.to_s[1..-1].to_sym, s(:self)]
159
+ else
160
+ [value.to_sym, s(:autobind, s(:self))]
161
+ end
162
+ }.to_h
163
+
164
+ @namespace.defineProps values, [node.children.last]
165
+ end
132
166
  end
133
167
 
134
168
  super
@@ -1,5 +1,5 @@
1
1
  require 'ruby2js'
2
- require 'regexp_parser'
2
+ require 'regexp_parser/scanner'
3
3
 
4
4
  module Ruby2JS
5
5
  module Filter
@@ -49,71 +49,78 @@ module Ruby2JS
49
49
 
50
50
  elsif method == :[]= and args.length == 3 and
51
51
  args[0].type == :regexp and args[1].type == :int
52
+
52
53
  index = args[1].children.first
53
54
 
54
- parts = Regexp::Parser.parse(args[0].children.first.children.first)
55
- split = parts.index do |part|
56
- part.type == :group and part.number == index
55
+ # identify groups
56
+ regex = args[0].children.first.children.first
57
+ tokens = Regexp::Scanner.scan(regex)
58
+ groups = []
59
+ stack = []
60
+ tokens.each do |token|
61
+ next unless token[0] == :group
62
+ if token[1] == :capture
63
+ groups.push token.dup
64
+ return super if groups.length == index and not stack.empty?
65
+ stack.push groups.last
66
+ elsif token[1] == :close
67
+ stack.pop[-1]=token.last
68
+ end
57
69
  end
70
+ group = groups[index-1]
58
71
 
59
- return super unless split
72
+ # rewrite regex
73
+ prepend = nil
74
+ append = nil
60
75
 
61
- rewritten = parts[split].to_s
62
-
63
- dstr = [args.last]
64
- if rewritten == '()'
65
- index -= 1
66
- rewritten = ''
76
+ if group[4] < regex.length
77
+ regex = (regex[0...group[4]] + '(' + regex[group[4]..-1] + ')').
78
+ sub(/\$\)$/, ')$')
79
+ append = 2
67
80
  end
68
81
 
69
- parts = parts.to_a
70
-
71
- pre = ''
72
- if parts.first.type == :anchor
73
- pre = parts.shift.to_s
74
- split -= 1
82
+ if group[4] - group[3] == 2
83
+ regex = regex[0...group[3]] + regex[group[4]..-1]
84
+ append = 1 if append
75
85
  end
76
86
 
77
- if split > 0
78
- if split == 1 and parts.first.type == :group
79
- rewritten = parts.first.to_s + rewritten
80
- else
81
- rewritten = '(' + parts[0 .. split - 1].join + ')' + rewritten
82
- index += 1
83
- end
84
- dstr.unshift s(:send, s(:lvar, :match), :[], s(:int, 0))
87
+ if group[3] > 0
88
+ regex = ('(' + regex[0...group[3]] + ')' + regex[group[3]..-1]).
89
+ sub(/^\(\^/, '^(')
90
+ prepend = 1
91
+ append += 1 if append
85
92
  end
86
93
 
94
+ regex = process s(:regexp, s(:str, regex), args[0].children.last)
87
95
 
88
- post = ''
89
- if parts.last.type == :anchor
90
- post = parts.pop.to_s
91
- end
92
-
93
- if split + 1 < parts.length
94
- if split + 2 == parts.length and parts.last.type == :group
95
- rewritten += parts.last.to_s
96
- else
97
- rewritten += '(' + parts[split + 1 .. -1].join + ')'
96
+ #
97
+ if args.last.type == :str
98
+ str = args.last.children.first.gsub('$', '$$')
99
+ str = "$#{prepend}#{str}" if prepend
100
+ str = "#{str}$#{append}" if append
101
+ expr = s(:send, target, :replace, regex, s(:str, str))
102
+ else
103
+ dstr = args.last.type == :dstr ? args.last.children.dup : [args.last]
104
+ if prepend
105
+ dstr.unshift s(:send, s(:lvar, :match), :[], s(:int, prepend-1))
106
+ end
107
+ if append
108
+ dstr << s(:send, s(:lvar, :match), :[], s(:int, append-1))
98
109
  end
99
- dstr << s(:send, s(:lvar, :match), :[], s(:int, index))
100
- end
101
-
102
- rewritten = pre + rewritten + post
103
110
 
104
- regex = process s(:regexp, s(:str, rewritten), args[0].children.last)
105
- block = s(:block,
106
- s(:send, target, :replace, regex),
107
- s(:args, s(:arg, :match)),
108
- process(s(:dstr, *dstr)))
111
+ expr = s(:block,
112
+ s(:send, target, :replace, regex),
113
+ s(:args, s(:arg, :match)),
114
+ process(s(:dstr, *dstr)))
115
+ end
109
116
 
110
117
  if VAR_TO_ASSIGN.keys.include? target.type
111
- S(VAR_TO_ASSIGN[target.type], target.children.first, block)
118
+ S(VAR_TO_ASSIGN[target.type], target.children.first, expr)
112
119
  elsif target.type == :send
113
120
  if target.children[0] == nil
114
- S(:lvasgn, target.children[1], block)
121
+ S(:lvasgn, target.children[1], expr)
115
122
  else
116
- S(:send, target.children[0], :"#{target.children[1]}=", block)
123
+ S(:send, target.children[0], :"#{target.children[1]}=", expr)
117
124
  end
118
125
  else
119
126
  super
@@ -122,57 +129,14 @@ module Ruby2JS
122
129
  elsif method == :merge
123
130
  args.unshift target
124
131
 
125
- if es2015
126
- if es2018
127
- process S(:hash, *args.map {|arg| s(:kwsplat, arg)})
128
- else
129
- process S(:send, s(:const, nil, :Object), :assign, s(:hash),
130
- *args)
131
- end
132
+ if es2018
133
+ process S(:hash, *args.map {|arg| s(:kwsplat, arg)})
132
134
  else
133
- copy = [s(:gvasgn, :$$, s(:hash))]
134
-
135
- s(:send, s(:block, s(:send, nil, :lambda), s(:args),
136
- s(:begin, *copy, *args.map {|modname|
137
- if modname.type == :hash
138
- s(:begin, *modname.children.map {|pair|
139
- s(:send, s(:gvar, :$$), :[]=, *pair.children)
140
- })
141
- else
142
- s(:for, s(:lvasgn, :$_), modname,
143
- s(:send, s(:gvar, :$$), :[]=,
144
- s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
145
- end
146
- }, s(:return, s(:gvar, :$$)))), :[])
135
+ process S(:assign, s(:hash), *args)
147
136
  end
148
137
 
149
138
  elsif method == :merge!
150
- if es2015
151
- process S(:send, s(:const, nil, :Object), :assign, target, *args)
152
- else
153
- copy = []
154
-
155
- unless
156
- target.type == :send and target.children.length == 2 and
157
- target.children[0] == nil
158
- then
159
- copy << s(:gvasgn, :$0, target)
160
- target = s(:gvar, :$0)
161
- end
162
-
163
- s(:send, s(:block, s(:send, nil, :lambda), s(:args),
164
- s(:begin, *copy, *args.map {|modname|
165
- if modname.type == :hash
166
- s(:begin, *modname.children.map {|pair|
167
- s(:send, target, :[]=, *pair.children)
168
- })
169
- else
170
- s(:for, s(:lvasgn, :$_), modname,
171
- s(:send, target, :[]=,
172
- s(:lvar, :$_), s(:send, modname, :[], s(:lvar, :$_))))
173
- end
174
- }, s(:return, target))), :[])
175
- end
139
+ process S(:assign, target, *args)
176
140
 
177
141
  elsif method == :delete and args.length == 1
178
142
  if not target
@@ -551,6 +515,20 @@ module Ruby2JS
551
515
  s(:args, s(:arg, :a), s(:arg, :b)),
552
516
  s(:send, s(:lvar, :a), :+, s(:lvar, :b))), s(:int, 0))
553
517
 
518
+ elsif method == :method_defined? and args.length >= 1
519
+ if args[1] == s(:false)
520
+ process S(:send, s(:attr, target, :prototype), :hasOwnProperty, args[0])
521
+ elsif args.length == 1 or args[1] == s(:true)
522
+ process S(:in?, args[0], s(:attr, target, :prototype))
523
+ else
524
+ process S(:if, args[1], s(:in?, args[0], s(:attr, target, :prototype)),
525
+ s(:send, s(:attr, target, :prototype), :hasOwnProperty, args[0]))
526
+ end
527
+
528
+ elsif method == :alias_method and args.length == 2
529
+ process S(:send, s(:attr, target, :prototype), :[]=, args[0],
530
+ s(:attr, s(:attr, target, :prototype), args[1].children[0]))
531
+
554
532
  else
555
533
  super
556
534
  end
@@ -737,6 +715,10 @@ module Ruby2JS
737
715
  s(:return, s(:lvar, node.children[1].children[0].children[0])))),
738
716
  :[], call.children[0]])
739
717
 
718
+ elsif method == :define_method and call.children.length == 3
719
+ process node.updated(:send, [s(:attr, call.children[0], :prototype), :[]=,
720
+ call.children[2], s(:deff, nil, *node.children[1..-1])])
721
+
740
722
  else
741
723
  super
742
724
  end
@@ -746,6 +728,12 @@ module Ruby2JS
746
728
  name, inheritance, *body = node.children
747
729
  body.compact!
748
730
 
731
+ body.each_with_index do |node, i|
732
+ if node.type == :send and node.children[0..1] == [nil, :alias_method]
733
+ body[i] = node.updated(:send, [name, *node.children[1..-1]])
734
+ end
735
+ end
736
+
749
737
  if inheritance == s(:const, nil, :Exception)
750
738
  unless
751
739
  body.any? {|statement| statement.type == :def and
@@ -761,7 +749,8 @@ module Ruby2JS
761
749
  body = [s(:begin, *body)] if body.length > 1
762
750
  S(:class, name, s(:const, nil, :Error), *body)
763
751
  else
764
- super
752
+ body = [s(:begin, *body)] if body.length > 1
753
+ super S(:class, name, inheritance, *body)
765
754
  end
766
755
  end
767
756
  end