ruby2js 3.4.0 → 3.6.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.
@@ -9,11 +9,22 @@ module Ruby2JS
9
9
  # NOTE: this is the es2015 version of class
10
10
 
11
11
  handle :class2 do |name, inheritance, *body|
12
+ body.compact!
13
+ while body.length == 1 and body.first.type == :begin
14
+ body = body.first.children
15
+ end
16
+
17
+ proxied = body.find do |node|
18
+ node.type == :def and node.children.first == :method_missing
19
+ end
20
+
12
21
  if name.type == :const and name.children.first == nil
13
22
  put 'class '
14
23
  parse name
24
+ put '$' if proxied
15
25
  else
16
26
  parse name
27
+ put '$' if proxied
17
28
  put ' = class'
18
29
  end
19
30
 
@@ -24,11 +35,6 @@ module Ruby2JS
24
35
 
25
36
  put " {"
26
37
 
27
- body.compact!
28
- while body.length == 1 and body.first.type == :begin
29
- body = body.first.children
30
- end
31
-
32
38
  begin
33
39
  class_name, @class_name = @class_name, name
34
40
  class_parent, @class_parent = @class_parent, inheritance
@@ -64,21 +70,21 @@ module Ruby2JS
64
70
  walk[child] if child.is_a? Parser::AST::Node
65
71
  end
66
72
 
67
- if ast.type == :send and ast.children.first == nil
68
- if ast.children[1] == :attr_accessor
69
- ast.children[2..-1].each_with_index do |child_sym, index2|
70
- ivars << :"@#{child_sym.children.first}"
71
- end
72
- elsif ast.children[1] == :attr_reader
73
- ast.children[2..-1].each_with_index do |child_sym, index2|
74
- ivars << :"@#{child_sym.children.first}"
75
- end
76
- elsif ast.children[1] == :attr_writer
77
- ast.children[2..-1].each_with_index do |child_sym, index2|
78
- ivars << :"@#{child_sym.children.first}"
79
- end
80
- end
81
- end
73
+ if ast.type == :send and ast.children.first == nil
74
+ if ast.children[1] == :attr_accessor
75
+ ast.children[2..-1].each_with_index do |child_sym, index2|
76
+ ivars << :"@#{child_sym.children.first}"
77
+ end
78
+ elsif ast.children[1] == :attr_reader
79
+ ast.children[2..-1].each_with_index do |child_sym, index2|
80
+ ivars << :"@#{child_sym.children.first}"
81
+ end
82
+ elsif ast.children[1] == :attr_writer
83
+ ast.children[2..-1].each_with_index do |child_sym, index2|
84
+ ivars << :"@#{child_sym.children.first}"
85
+ end
86
+ end
87
+ end
82
88
 
83
89
  end
84
90
  walk[@ast]
@@ -299,6 +305,43 @@ module Ruby2JS
299
305
  end
300
306
  end
301
307
 
308
+ if proxied
309
+ put @sep
310
+
311
+ rename = name.updated(nil, [name.children.first, name.children.last.to_s + '$'])
312
+
313
+ if proxied.children[1].children.length == 1
314
+ # special case: if method_missing only has on argument, call it
315
+ # directly (i.e., don't pass arguments). This enables
316
+ # method_missing to return instance attributes (getters) as well
317
+ # as bound functions (methods).
318
+ forward = s(:send, s(:lvar, :obj), :method_missing, s(:lvar, :prop))
319
+ else
320
+ # normal case: return a function which, when called, will call
321
+ # method_missing with method name and arguments.
322
+ forward = s(:block, s(:send, nil, :proc), s(:args, s(:restarg, :args)),
323
+ s(:send, s(:lvar, :obj), :method_missing, s(:lvar, :prop),
324
+ s(:splat, s(:lvar, :args))))
325
+ end
326
+
327
+ proxy = s(:return, s(:send, s(:const, nil, :Proxy), :new,
328
+ s(:send, rename, :new, s(:splat, s(:lvar, :args))),
329
+ s(:hash, s(:pair, s(:sym, :get), s(:block, s(:send, nil, :proc),
330
+ s(:args, s(:arg, :obj), s(:arg, :prop)),
331
+ s(:if, s(:in?, s(:lvar, :prop), s(:lvar, :obj)),
332
+ s(:return, s(:send, s(:lvar, :obj), :[], s(:lvar, :prop))),
333
+ s(:return, forward))))))
334
+ )
335
+
336
+ if name.children.first == nil
337
+ proxy = s(:def, name.children.last, s(:args, s(:restarg, :args)), proxy)
338
+ else
339
+ proxy = s(:defs, *name.children, s(:args, s(:restarg, :args)), proxy)
340
+ end
341
+
342
+ parse proxy
343
+ end
344
+
302
345
  ensure
303
346
  @class_name = class_name
304
347
  @class_parent = class_parent
@@ -131,7 +131,7 @@ module Ruby2JS
131
131
  style = :statement
132
132
  end
133
133
 
134
- if args.children.length == 1 and style == :expression
134
+ if args.children.length == 1 and args.children.first.type != :restarg and style == :expression
135
135
  parse args; put ' => '
136
136
  else
137
137
  put '('; parse args; put ') => '
@@ -6,7 +6,23 @@ module Ruby2JS
6
6
  # NOTE: import is a synthetic
7
7
 
8
8
  handle :import do |path, *args|
9
+ if module_type == :cjs
10
+ # only the subset of import syntaxes generated by filters are handled here
11
+ if Parser::AST::Node === args.first and args.first.type == :attr
12
+ return parse s(:casgn, *args.first.children,
13
+ s(:send, nil, :require, s(:str, Array(path).first))), :statement
14
+ elsif Array === args.first and args.first.length == 1
15
+ target = args.first.first
16
+ if Parser::AST::Node === target and target.type == :attr and target.children.first == nil
17
+ return parse s(:casgn, *target.children,
18
+ s(:attr, s(:send, nil, :require, s(:str, Array(path).first)), target.children.last)),
19
+ :statement
20
+ end
21
+ end
22
+ end
23
+
9
24
  put 'import '
25
+
10
26
  if args.length == 0
11
27
  # import "file.css"
12
28
  put path.inspect
@@ -66,7 +82,7 @@ module Ruby2JS
66
82
  elsif node.respond_to?(:type) && node.children[1] == :default
67
83
  put 'default '
68
84
  args[0] = node.children[2]
69
- elsif node.respond_to?(:type) && node.type == :lvasgn
85
+ elsif node.respond_to?(:type) && [:lvasgn, :casgn].include?(node.type)
70
86
  if node.children[0] == :default
71
87
  put 'default '
72
88
  args[0] = node.children[1]
@@ -16,8 +16,16 @@ module Ruby2JS
16
16
  handle :hostvalue do |value|
17
17
  case value
18
18
  when Hash
19
- parse s(:hash, *value.map {|key, hvalue| s(:pair, s(:hostvalue, key),
20
- s(:hostvalue, hvalue))})
19
+ parse s(:hash, *value.map {|key, hvalue|
20
+ case key
21
+ when String
22
+ s(:pair, s(:str, key), s(:hostvalue, hvalue))
23
+ when Symbol
24
+ s(:pair, s(:sym, key), s(:hostvalue, hvalue))
25
+ else
26
+ s(:pair, s(:hostvalue, key), s(:hostvalue, hvalue))
27
+ end
28
+ })
21
29
  when Array
22
30
  parse s(:array, *value.map {|hvalue| s(:hostvalue, hvalue)})
23
31
  when String
@@ -5,12 +5,24 @@ module Ruby2JS
5
5
  # (float 1.1)
6
6
  # (str "1"))
7
7
 
8
- handle :int, :float, :str do |value|
8
+ handle :str do |value|
9
9
  put value.inspect
10
10
  end
11
11
 
12
+ handle :int, :float do |value|
13
+ put number_format(value)
14
+ end
15
+
12
16
  handle :octal do |value|
13
- put '0' + value.to_s(8)
17
+ put '0' + number_format(value.to_s(8))
18
+ end
19
+
20
+ def number_format(number)
21
+ return number.to_s unless es2021
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]
25
+ parts.join('.')
14
26
  end
15
27
  end
16
28
  end
@@ -9,11 +9,12 @@ module Ruby2JS
9
9
  # (sendw nil :puts
10
10
  # (int 1))
11
11
 
12
- # Note: attr, sendw, and await are only generated by filters. Attr forces
12
+ # Note: attr, sendw, send!, and await are only generated by filters. Attr forces
13
13
  # interpretation as an attribute vs a function call with zero parameters.
14
+ # send! forces interpretation as a method call even with zero parameters.
14
15
  # Sendw forces parameters to be placed on separate lines.
15
16
 
16
- handle :send, :sendw, :await, :attr, :call do |receiver, method, *args|
17
+ handle :send, :sendw, :send!, :await, :attr, :call do |receiver, method, *args|
17
18
  ast = @ast
18
19
 
19
20
  if \
@@ -280,7 +281,7 @@ module Ruby2JS
280
281
  else
281
282
  put 'await ' if @ast.type == :await
282
283
 
283
- if not ast.is_method?
284
+ if not ast.is_method? and ast.type != :send!
284
285
  if receiver
285
286
  (group_receiver ? group(receiver) : parse(receiver))
286
287
  put ".#{ method }"
@@ -10,7 +10,7 @@ module Ruby2JS
10
10
  if @binding
11
11
  puts @binding.eval(str).to_s
12
12
  else
13
- puts eval(str).to_s
13
+ raise SecurityError.new('Insecure operation, eval without binding option')
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,43 @@
1
+ require 'ruby2js'
2
+
3
+ module Ruby2JS
4
+ module Filter
5
+ module ActiveFunctions
6
+ include SEXP
7
+
8
+ def on_send(node)
9
+ target, method, *args = node.children
10
+
11
+ if es2015 and method == :blank?
12
+ create_or_update_import("blank$")
13
+ process node.updated :send, [nil, "blank$", target]
14
+ elsif es2015 and method == :present?
15
+ create_or_update_import("present$")
16
+ process node.updated :send, [nil, "present$", target]
17
+ elsif es2015 and method == :presence
18
+ create_or_update_import("presence$")
19
+ process node.updated :send, [nil, "presence$", target]
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def create_or_update_import(token)
28
+ af_import = @options[:import_from_skypack] ? "https://cdn.skypack.dev/@ruby2js/active-functions" : "@ruby2js/active-functions"
29
+
30
+ if found_node = prepend_list.find {|ast| ast.type == :import && ast.children.first == af_import}
31
+ unless found_node.children.find {|child| child == token}
32
+ prepend_list.delete found_node
33
+ prepend_list << s(:import, found_node.children.first, found_node.children.last.push(s(:const, nil, token)))
34
+ end
35
+ else
36
+ prepend_list << s(:import, af_import, [s(:const, nil, token)])
37
+ end
38
+ end
39
+ end
40
+
41
+ DEFAULTS.push ActiveFunctions
42
+ end
43
+ end
@@ -9,8 +9,9 @@ module Ruby2JS
9
9
  module CamelCase
10
10
  include SEXP
11
11
 
12
- WHITELIST = %w{
12
+ ALLOWLIST = %w{
13
13
  attr_accessor
14
+ method_missing
14
15
  }
15
16
 
16
17
  CAPS_EXCEPTIONS = {
@@ -37,7 +38,7 @@ module Ruby2JS
37
38
  node = super
38
39
  return node unless [:send, :csend, :attr].include? node.type
39
40
 
40
- if node.children[0] == nil and WHITELIST.include? node.children[1].to_s
41
+ if node.children[0] == nil and ALLOWLIST.include? node.children[1].to_s
41
42
  node
42
43
  elsif node.children[0] && [:ivar, :cvar].include?(node.children[0].type)
43
44
  S(node.type, s(node.children[0].type, camelCase(node.children[0].children[0])),
@@ -61,7 +62,7 @@ module Ruby2JS
61
62
  def handle_generic_node(node, node_type)
62
63
  return node if node.type != node_type
63
64
 
64
- if node.children[0] =~ /_.*\w$/
65
+ if node.children[0] =~ /_.*\w$/ and !ALLOWLIST.include?(node.children[0].to_s)
65
66
  S(node_type , camelCase(node.children[0]), *node.children[1..-1])
66
67
  else
67
68
  node
@@ -1,13 +1,68 @@
1
1
  require 'ruby2js'
2
2
 
3
+ Ruby2JS.module_default = :esm
4
+
3
5
  module Ruby2JS
4
6
  module Filter
5
7
  module ESM
6
8
  include SEXP
7
9
 
8
- def initialize(*args)
10
+ def options=(options)
11
+ super
12
+ @esm_autoexports = options[:autoexports] && !@disable_autoexports
13
+ @esm_autoimports = options[:autoimports]
14
+ @esm_explicit_tokens = Set.new
15
+ end
16
+
17
+ def process(node)
18
+ return super unless @esm_autoexports
19
+ @esm_autoexports = false
20
+
21
+ list = [node]
22
+ while list.length == 1 and list.first.type == :begin
23
+ list = list.first.children.dup
24
+ end
25
+
26
+ list.map! do |child|
27
+ replacement = child
28
+
29
+ if [:module, :class].include? child.type and
30
+ child.children.first.type == :const and
31
+ child.children.first.children.first == nil \
32
+ then
33
+ replacement = s(:export, child)
34
+ elsif child.type == :casgn and child.children.first == nil
35
+ replacement = s(:export, child)
36
+ elsif child.type == :def
37
+ replacement = s(:export, child)
38
+ end
39
+
40
+ if replacement != child and @comments[child]
41
+ @comments[replacement] = @comments[child]
42
+ end
43
+
44
+ replacement
45
+ end
46
+
47
+ process s(:begin, *list)
48
+ end
49
+
50
+ def on_class(node)
51
+ @esm_explicit_tokens << node.children.first.children.last
52
+
53
+ super
54
+ end
55
+
56
+ def on_def(node)
57
+ @esm_explicit_tokens << node.children.first
58
+
59
+ super
60
+ end
61
+
62
+ def on_lvasgn(node)
63
+ @esm_explicit_tokens << node.children.first
64
+
9
65
  super
10
- @esm = true # signal for other filters
11
66
  end
12
67
 
13
68
  def on_send(node)
@@ -36,6 +91,8 @@ module Ruby2JS
36
91
  args[0].children[2].children[2].type == :str
37
92
  # import name from "file.js"
38
93
  # => import name from "file.js"
94
+ @esm_explicit_tokens << args[0].children[1]
95
+
39
96
  s(:import,
40
97
  [args[0].children[2].children[2].children[0]],
41
98
  process(s(:attr, nil, args[0].children[1])))
@@ -49,17 +106,52 @@ module Ruby2JS
49
106
  # => import Stuff as * from "file.js"
50
107
  # import [ Some, Stuff ], from: "file.js"
51
108
  # => import { Some, Stuff } from "file.js"
52
- imports = (args[0].type == :const || args[0].type == :send) ?
53
- process(args[0]) :
109
+ imports = if args[0].type == :const || args[0].type == :send
110
+ @esm_explicit_tokens << args[0].children.last
111
+ process(args[0])
112
+ else
113
+ args[0].children.each {|i| @esm_explicit_tokens << i.children.last}
54
114
  process_all(args[0].children)
115
+ end
116
+
55
117
  s(:import, args[1].children, imports) unless args[1].nil?
56
118
  end
57
119
  elsif method == :export
58
120
  s(:export, *process_all(args))
121
+ elsif target.nil? and found_import = find_autoimport(method)
122
+ prepend_list << s(:import, found_import[0], found_import[1])
123
+ super
59
124
  else
60
125
  super
61
126
  end
62
127
  end
128
+
129
+ def on_const(node)
130
+ if node.children.first == nil and found_import = find_autoimport(node.children.last)
131
+ prepend_list << s(:import, found_import[0], found_import[1])
132
+ end
133
+
134
+ super
135
+ end
136
+
137
+ def on_export(node)
138
+ s(:export, *process_all(node.children))
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def find_autoimport(token)
145
+ return nil if @esm_autoimports.nil?
146
+ return nil if @esm_explicit_tokens.include?(token)
147
+
148
+ token = camelCase(token) if respond_to?(:camelCase)
149
+
150
+ if @esm_autoimports[token]
151
+ [@esm_autoimports[token], s(:const, nil, token)]
152
+ elsif found_key = @esm_autoimports.keys.find {|key| key.is_a?(Array) && key.include?(token)}
153
+ [@esm_autoimports[found_key], found_key.map {|key| s(:const, nil, key)}]
154
+ end
63
155
  end
64
156
 
65
157
  DEFAULTS.push ESM
@@ -531,6 +531,20 @@ module Ruby2JS
531
531
  elsif method == :block_given? and target == nil and args.length == 0
532
532
  process process s(:lvar, "_implicitBlockYield")
533
533
 
534
+ elsif method == :abs and args.length == 0
535
+ process S(:send, s(:const, nil, :Math), :abs, target)
536
+
537
+ elsif method == :ceil and args.length == 0
538
+ process S(:send, s(:const, nil, :Math), :ceil, target)
539
+
540
+ elsif method == :floor and args.length == 0
541
+ process S(:send, s(:const, nil, :Math), :floor, target)
542
+
543
+ elsif method == :sum and args.length == 0
544
+ process S(:send, target, :reduce, s(:block, s(:send, nil, :proc),
545
+ s(:args, s(:arg, :a), s(:arg, :b)),
546
+ s(:send, s(:lvar, :a), :+, s(:lvar, :b))), s(:int, 0))
547
+
534
548
  else
535
549
  super
536
550
  end