ruby2js 3.3.5 → 3.5.2

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,6 +9,22 @@ module Ruby2JS
9
9
  handle :def, :defm, :async do |name, args, body=nil|
10
10
  body ||= s(:begin)
11
11
 
12
+ add_implicit_block = false
13
+
14
+ walk = ->(node) do
15
+ add_implicit_block = true if node.type == :yield || (node.type == :send && node.children[1] == "_implicitBlockYield")
16
+ node.children.each do |child|
17
+ walk[child] if child.is_a? Parser::AST::Node
18
+ end
19
+ end
20
+ walk[body]
21
+
22
+ if add_implicit_block
23
+ children = args.children.dup
24
+ children.push s(:optarg, "_implicitBlockYield", s(:nil))
25
+ args = s(:args, *children)
26
+ end
27
+
12
28
  vars = {}
13
29
  vars.merge! @vars unless name
14
30
  if args and !args.children.empty?
@@ -20,7 +20,7 @@ module Ruby2JS
20
20
  put '`'
21
21
  children.each do |child|
22
22
  if child.type == :str
23
- str = child.children.first.inspect[1..-2].gsub('${', '$\{')
23
+ str = child.children.first.inspect[1..-2].gsub('${', '$\{').gsub('`', '\\\`')
24
24
  if heredoc
25
25
  put! str.gsub("\\n", "\n")
26
26
  else
@@ -97,10 +97,12 @@ module Ruby2JS
97
97
  end
98
98
 
99
99
  # use fat arrow syntax if block contains a reference to 'this'
100
- if anonfn
100
+ if anonfn and @class_name
101
101
  walk = proc do |ast|
102
102
  if ast == s(:self)
103
103
  anonfn = false
104
+ elsif [:ivar, :ivasgn].include? ast.type
105
+ anonfn = false
104
106
  elsif ast.type == :send and ast.children.first == nil
105
107
  method = ast.children.last if ast.children.length == 2
106
108
  if @rbstack.any? {|rb| rb[method]} or method == :this
@@ -6,15 +6,64 @@ 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 '
10
25
 
11
- args.each_with_index do |arg, index|
12
- put ', ' unless index == 0
13
- parse arg
14
- end
26
+ if args.length == 0
27
+ # import "file.css"
28
+ put path.inspect
29
+ else
30
+ # import (x) from "file.js"
31
+ default_import = !args.first.is_a?(Array) && [:const, :send, :attr].include?(args.first.type)
32
+ args = args.first if args.first.is_a?(Array)
33
+
34
+ # handle the default name or { ConstA, Const B } portion
35
+ put "{ " unless default_import
36
+ args.each_with_index do |arg, index|
37
+ put ', ' unless index == 0
38
+ parse arg
39
+ end
40
+ put " }" unless default_import
41
+
42
+ from_kwarg_position = 0
43
+
44
+ # should there be an as clause? e.g., import React as *
45
+ 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]}"
15
47
 
16
- put ' from '
17
- put path.inspect
48
+ # advance to the next kwarg, aka from
49
+ from_kwarg_position = 1
50
+ end
51
+
52
+ put ' from '
53
+
54
+ if path.is_a?(Array) && !path[from_kwarg_position].is_a?(String) && path[from_kwarg_position].type == :pair
55
+ # from: "str" => from "str"
56
+ if path[from_kwarg_position].children[0].children[0] == :from
57
+ put path[from_kwarg_position].children[1].children[0].inspect
58
+ else
59
+ # from is missing
60
+ put '""'
61
+ end
62
+ else
63
+ # handle a str in either an array element or directly passed in
64
+ put path.is_a?(Array) ? path[0].inspect : path.inspect
65
+ end
66
+ end
18
67
  end
19
68
 
20
69
  # (export const)
@@ -24,14 +73,49 @@ module Ruby2JS
24
73
  handle :export do |*args|
25
74
  put 'export '
26
75
 
27
- if args.first == :default
76
+ node = args.first
77
+ final_export = false
78
+
79
+ if node == :default
28
80
  put 'default '
29
81
  args.shift
82
+ elsif node.respond_to?(:type) && node.children[1] == :default
83
+ put 'default '
84
+ args[0] = node.children[2]
85
+ elsif node.respond_to?(:type) && node.type == :lvasgn
86
+ if node.children[0] == :default
87
+ put 'default '
88
+ args[0] = node.children[1]
89
+ else
90
+ put 'const '
91
+ end
92
+ elsif node.respond_to?(:type) &&
93
+ node.type == :array &&
94
+ node.children[0].respond_to?(:type) &&
95
+ (
96
+ node.children[0].type == :const ||
97
+ node.children[0].type == :send ||
98
+ (node.children[0].type == :hash && node.children[0].children[0].children[0].children[0] == :default )
99
+ )
100
+ final_export = true
101
+ put '{ '
102
+ node.children.each_with_index do |arg, index|
103
+ put ', ' unless index == 0
104
+ if arg.type == :hash && arg.children[0].children[0].children[0] == :default
105
+ put arg.children[0].children[1].children[1]
106
+ put ' as default'
107
+ else
108
+ parse arg
109
+ end
110
+ end
111
+ put ' }'
30
112
  end
31
113
 
32
- args.each_with_index do |arg, index|
33
- put ', ' unless index == 0
34
- parse arg
114
+ unless final_export
115
+ args.each_with_index do |arg, index|
116
+ put ', ' unless index == 0
117
+ parse arg
118
+ end
35
119
  end
36
120
  end
37
121
  end
@@ -6,18 +6,26 @@ module Ruby2JS
6
6
  handle :ivar do |var|
7
7
  if self.ivars and self.ivars.include? var
8
8
  parse s(:hostvalue, self.ivars[var])
9
- elsif es2020
10
- parse s(:attr, s(:self), var.to_s.sub('@', '#'))
11
- else
9
+ elsif underscored_private
12
10
  parse s(:attr, s(:self), var.to_s.sub('@', '_'))
11
+ else
12
+ parse s(:attr, s(:self), var.to_s.sub('@', '#'))
13
13
  end
14
14
  end
15
15
 
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
@@ -7,7 +7,7 @@ module Ruby2JS
7
7
  handle :ivasgn do |var, expression=nil|
8
8
  multi_assign_declarations if @state == :statement
9
9
 
10
- put "#{ var.to_s.sub('@', 'this.' + (es2020 ? '#' : '_')) }"
10
+ put "#{ var.to_s.sub('@', 'this.' + (underscored_private ? '_' : '#')) }"
11
11
  if expression
12
12
  put " = "; parse expression
13
13
  end
@@ -89,7 +89,7 @@ module Ruby2JS
89
89
  def conditionally_equals(left, right)
90
90
  if left == right
91
91
  true
92
- elsif !left or !right or left.type != :csend or right.type != :send
92
+ elsif !left.respond_to?(:type) or !left or !right or left.type != :csend or right.type != :send
93
93
  false
94
94
  else
95
95
  conditionally_equals(left.children.first, right.children.first) &&
@@ -14,7 +14,7 @@ module Ruby2JS
14
14
 
15
15
  EXPRESSIONS = [ :array, :float, :hash, :int, :lvar, :nil, :send, :attr,
16
16
  :str, :sym, :dstr, :dsym, :cvar, :ivar, :zsuper, :super, :or, :and,
17
- :block, :const, :true, :false, :xnode ]
17
+ :block, :const, :true, :false, :xnode, :taglit, :self ]
18
18
 
19
19
  handle :autoreturn do |*statements|
20
20
  return if statements == [nil]
@@ -55,6 +55,8 @@ module Ruby2JS
55
55
  node = block.pop
56
56
  children = node.children.dup
57
57
  (1...children.length).each do |i|
58
+ next if children[i].nil? # case statements without else clause end with nil
59
+
58
60
  if children[i].type == :when
59
61
  gchildren = children[i].children.dup
60
62
  if !gchildren.empty? and EXPRESSIONS.include? gchildren.last.type
@@ -0,0 +1,13 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (taglit
5
+ # (arg :tag)
6
+ # (dstr)
7
+
8
+ handle :taglit do |tag, *children|
9
+ put tag.children.first
10
+ parse_all(*children, join: '')
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Ruby2JS
2
+ class Converter
3
+
4
+ # (yield
5
+ # (arg 'a'))
6
+
7
+ handle :yield do |*args|
8
+ put '_implicitBlockYield'
9
+ put "("; parse_all(*args, join: ', '); put ')'
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,9 @@
1
1
  require 'ruby2js'
2
2
 
3
+ # Note care is taken to run all the filters first before camelCasing.
4
+ # This ensures that Ruby methods like each_pair can be mapped to
5
+ # JavaScript before camelcasing.
6
+
3
7
  module Ruby2JS
4
8
  module Filter
5
9
  module CamelCase
@@ -9,75 +13,114 @@ module Ruby2JS
9
13
  attr_accessor
10
14
  }
11
15
 
16
+ CAPS_EXCEPTIONS = {
17
+ "innerHtml" => "innerHTML",
18
+ "innerHtml=" => "innerHTML=",
19
+ "outerHtml" => "outerHTML",
20
+ "outerHtml=" => "outerHTML=",
21
+ "encodeUri" => "encodeURI",
22
+ "encodeUriComponent" => "encodeURIComponent",
23
+ "decodeUri" => "decodeURI",
24
+ "decodeUriComponent" => "decodeURIComponent"
25
+ }
26
+
12
27
  def camelCase(symbol)
13
- symbol.to_s.gsub(/_[a-z]/) {|match| match[1].upcase}
28
+ should_symbolize = symbol.is_a?(Symbol)
29
+ symbol = symbol
30
+ .to_s
31
+ .gsub(/(?!^)_[a-z0-9]/) {|match| match[1].upcase}
32
+ .gsub(/^(.*)$/) {|match| CAPS_EXCEPTIONS[match] || match }
33
+ should_symbolize ? symbol.to_sym : symbol
14
34
  end
15
35
 
16
36
  def on_send(node)
37
+ node = super
38
+ return node unless [:send, :csend, :attr].include? node.type
39
+
17
40
  if node.children[0] == nil and WHITELIST.include? node.children[1].to_s
18
- super
19
- elsif node.children[1] =~ /_.*\w$/
20
- super S(:send , node.children[0],
41
+ node
42
+ elsif node.children[0] && [:ivar, :cvar].include?(node.children[0].type)
43
+ S(node.type, s(node.children[0].type, camelCase(node.children[0].children[0])),
44
+ camelCase(node.children[1]), *node.children[2..-1])
45
+ elsif node.children[1] =~ /_.*\w[=!?]?$/
46
+ S(node.type, node.children[0],
21
47
  camelCase(node.children[1]), *node.children[2..-1])
22
48
  else
23
- super
49
+ node
24
50
  end
25
51
  end
52
+
53
+ def on_csend(node)
54
+ on_send(node)
55
+ end
56
+
57
+ def on_attr(node)
58
+ on_send(node)
59
+ end
60
+
61
+ def handle_generic_node(node, node_type)
62
+ return node if node.type != node_type
26
63
 
27
- def on_def(node)
28
64
  if node.children[0] =~ /_.*\w$/
29
- super S(:def , camelCase(node.children[0]), *node.children[1..-1])
65
+ S(node_type , camelCase(node.children[0]), *node.children[1..-1])
30
66
  else
31
- super
67
+ node
32
68
  end
33
69
  end
34
70
 
71
+ def on_def(node)
72
+ handle_generic_node(super, :def)
73
+ end
74
+
35
75
  def on_optarg(node)
36
- if node.children[0] =~ /_.*\w$/
37
- super S(:optarg , camelCase(node.children[0]), *node.children[1..-1])
38
- else
39
- super
40
- end
76
+ handle_generic_node(super, :optarg)
77
+ end
78
+
79
+ def on_kwoptarg(node)
80
+ handle_generic_node(super, :kwoptarg)
41
81
  end
42
82
 
43
83
  def on_lvar(node)
44
- if node.children[0] =~ /_.*\w$/
45
- super S(:lvar , camelCase(node.children[0]), *node.children[1..-1])
46
- else
47
- super
48
- end
84
+ handle_generic_node(super, :lvar)
85
+ end
86
+
87
+ def on_ivar(node)
88
+ handle_generic_node(super, :ivar)
89
+ end
90
+
91
+ def on_cvar(node)
92
+ handle_generic_node(super, :cvar)
49
93
  end
50
94
 
51
95
  def on_arg(node)
52
- if node.children[0] =~ /_.*\w$/
53
- super S(:arg , camelCase(node.children[0]), *node.children[1..-1])
54
- else
55
- super
56
- end
96
+ handle_generic_node(super, :arg)
57
97
  end
58
98
 
59
99
  def on_lvasgn(node)
60
- if node.children[0] =~ /_.*\w$/
61
- super S(:lvasgn , camelCase(node.children[0]), *node.children[1..-1])
62
- else
63
- super
64
- end
100
+ handle_generic_node(super, :lvasgn)
101
+ end
102
+
103
+ def on_ivasgn(node)
104
+ handle_generic_node(super, :ivasgn)
105
+ end
106
+
107
+ def on_cvasgn(node)
108
+ handle_generic_node(super, :cvasgn)
65
109
  end
66
110
 
67
111
  def on_sym(node)
68
- if node.children[0] =~ /_.*\w$/
69
- super S(:sym , camelCase(node.children[0]), *node.children[1..-1])
70
- else
71
- super
72
- end
112
+ handle_generic_node(super, :sym)
73
113
  end
74
114
 
75
115
  def on_defs(node)
116
+ node = super
117
+ return node if node.type != :defs
118
+
76
119
  if node.children[1] =~ /_.*\w$/
77
- super S(:defs , node.children[0],
120
+ S(:defs , node.children[0],
78
121
  camelCase(node.children[1]), *node.children[2..-1])
79
122
  else
80
- super
123
+ node
81
124
  end
82
125
  end
83
126
  end
@@ -1,69 +1,98 @@
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
10
  def initialize(*args)
9
- @esm_include = nil
10
11
  super
12
+ @esm = true # signal for other filters
13
+ @esm_imports = nil
14
+ end
15
+
16
+ def options=(options)
17
+ super
18
+ @esm_autoimports = options[:autoimports]
19
+ return unless @esm_autoimports
11
20
  end
12
21
 
13
22
  def process(node)
14
- return super if @esm_include
15
- @esm_include = Set.new
16
- @esm_exclude = Set.new
17
- @esm_export = nil
23
+ return super if @esm_imports or not @esm_autoimports
24
+ @esm_imports = Set.new
18
25
  result = super
19
26
 
20
- esm_walk(result)
21
-
22
- inventory = (@esm_include - @esm_exclude).to_a.sort
23
-
24
- if inventory.empty? and not @esm_export
27
+ if @esm_imports.empty?
25
28
  result
26
29
  else
27
- list = inventory.map do |name|
28
- if name == "React" and defined? Ruby2JS::Filter::React
29
- s(:import, "#{name.downcase}", s(:const, nil, name))
30
- elsif not %w(JSON Object).include? name
31
- s(:import, "./#{name.downcase}.js", s(:const, nil, name))
32
- end
33
- end
30
+ s(:begin, *@esm_imports.to_a.map {|token|
31
+ s(:import, @esm_autoimports[token], s(:const, nil, token))
32
+ }, result)
33
+ end
34
+ end
34
35
 
35
- list.push result
36
+ def on_send(node)
37
+ target, method, *args = node.children
38
+ return super unless target.nil?
36
39
 
37
- if @esm_export
38
- list.push s(:export, :default, s(:const, nil, @esm_export))
40
+ if method == :import
41
+ # don't do the conversion if the word import is followed by a paren
42
+ if node.loc.respond_to? :selector
43
+ selector = node.loc.selector
44
+ if selector and selector.source_buffer
45
+ return super if selector.source_buffer.source[selector.end_pos] == '('
46
+ end
39
47
  end
40
48
 
41
- s(:begin, *list.compact)
42
- end
43
- end
49
+ if args[0].type == :str
50
+ # import "file.css"
51
+ # => import "file.css"
52
+ s(:import, args[0].children[0])
53
+ elsif args.length == 1 and \
54
+ args[0].type == :send and \
55
+ args[0].children[0].nil? and \
56
+ args[0].children[2].type == :send and \
57
+ args[0].children[2].children[0].nil? and \
58
+ args[0].children[2].children[1] == :from and \
59
+ args[0].children[2].children[2].type == :str
60
+ # import name from "file.js"
61
+ # => import name from "file.js"
62
+ s(:import,
63
+ [args[0].children[2].children[2].children[0]],
64
+ process(s(:attr, nil, args[0].children[1])))
44
65
 
45
- # gather constants
46
- def esm_walk(node)
47
- # extract ivars and cvars
48
- if node.type == :const and node.children.first == nil
49
- @esm_include << node.children.last.to_s
50
- elsif node.type == :xnode
51
- name = node.children.first
52
- @esm_include << name unless name.empty? or name =~ /^[a-z]/
53
- elsif node.type == :casgn and node.children.first == nil
54
- @esm_exclude << node.children[1].to_s
55
- elsif node.type == :class and node.children.first.type == :const
56
- if node.children.first.children.first == nil
57
- name = node.children.first.children.last.to_s
58
- @esm_exclude << name
59
- @esm_export ||= name
66
+ else
67
+ # import Stuff, "file.js"
68
+ # => import Stuff from "file.js"
69
+ # import Stuff, from: "file.js"
70
+ # => import Stuff from "file.js"
71
+ # import Stuff, as: "*", from: "file.js"
72
+ # => import Stuff as * from "file.js"
73
+ # import [ Some, Stuff ], from: "file.js"
74
+ # => import { Some, Stuff } from "file.js"
75
+ imports = (args[0].type == :const || args[0].type == :send) ?
76
+ process(args[0]) :
77
+ process_all(args[0].children)
78
+ s(:import, args[1].children, imports) unless args[1].nil?
60
79
  end
80
+ elsif method == :export
81
+ s(:export, *process_all(args))
82
+ elsif @esm_imports and args.length == 0 and @esm_autoimports[method]
83
+ @esm_imports.add(method)
84
+ super
85
+ else
86
+ super
61
87
  end
88
+ end
62
89
 
63
- # recurse
64
- node.children.each do |child|
65
- esm_walk(child) if Parser::AST::Node === child
90
+ def on_const(node)
91
+ return super unless @esm_autoimports
92
+ if node.children.first == nil and @esm_autoimports[node.children.last]
93
+ @esm_imports.add(node.children.last)
66
94
  end
95
+ super
67
96
  end
68
97
  end
69
98