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
@@ -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