Class: JsxRosetta::AST::Node
- Inherits:
-
Object
- Object
- JsxRosetta::AST::Node
show all
- Defined in:
- lib/jsx_rosetta/ast/node.rb
Overview
Base class for every Babel-shaped AST node. Wraps the raw JSON hash and provides:
* Field access via `node[:opening_element]` (snake_case symbols or
camelCase strings — both resolve to the same field).
* Source location accessors (`loc`, `range`, `start_pos`, `end_pos`).
* Tree traversal via `each_child` / `walk`.
* Pattern-matching support via `deconstruct_keys`.
Specific Babel node types may register subclasses that add named accessors (e.g. JSXElement#opening_element). Unknown types fall through to the generic Node class so the parser doesn’t crash on ESNext additions.
Direct Known Subclasses
File, JSXAttribute, JSXClosingElement, JSXClosingFragment, JSXElement, JSXEmptyExpression, JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXMemberExpression, JSXNamespacedName, JSXOpeningElement, JSXOpeningFragment, JSXSpreadAttribute, JSXSpreadChild, JSXText, Program
Constant Summary
collapse
- TYPE_REGISTRY =
rubocop:disable Style/MutableConstant
{}
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Constructor Details
#initialize(raw) ⇒ Node
Returns a new instance of Node.
44
45
46
|
# File 'lib/jsx_rosetta/ast/node.rb', line 44
def initialize(raw)
@raw = raw
end
|
Instance Attribute Details
#raw ⇒ Object
Returns the value of attribute raw.
22
23
24
|
# File 'lib/jsx_rosetta/ast/node.rb', line 22
def raw
@raw
end
|
Class Method Details
.matches?(value, *types) ⇒ Boolean
Defensive type predicate. True iff ‘value` is an AST::Node whose type is one of `types`. False for nil, arrays, strings, hashes, or any non-Node — making it safe for raw hash field accesses where the contents may be anything.
101
102
103
|
# File 'lib/jsx_rosetta/ast/node.rb', line 101
def self.matches?(value, *types)
value.is_a?(Node) && types.include?(value.type)
end
|
.register(*type_names) ⇒ Object
24
25
26
|
# File 'lib/jsx_rosetta/ast/node.rb', line 24
def self.register(*type_names)
type_names.each { |name| TYPE_REGISTRY[name] = self }
end
|
.wrap(value) ⇒ Object
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# File 'lib/jsx_rosetta/ast/node.rb', line 28
def self.wrap(value)
case value
when Hash
if value.key?("type")
klass = TYPE_REGISTRY.fetch(value["type"], Node)
klass.new(value)
else
value
end
when Array
value.map { |element| wrap(element) }
else
value
end
end
|
Instance Method Details
#==(other) ⇒ Object
Also known as:
eql?
146
147
148
|
# File 'lib/jsx_rosetta/ast/node.rb', line 146
def ==(other)
other.is_a?(Node) && other.raw == @raw
end
|
#[](key) ⇒ Object
Field access. Accepts snake_case symbols/strings (translated to camelCase) and camelCase strings (used verbatim). Returns whatever the raw hash contains at that key (Node, Array of Nodes, String, Hash, nil) — caller is responsible for type-checking. For typed access, prefer ‘#child` (returns Node | nil).
73
74
75
76
77
78
79
|
# File 'lib/jsx_rosetta/ast/node.rb', line 73
def [](key)
raw_key = key.to_s
return Node.wrap(@raw[raw_key]) if @raw.key?(raw_key)
camel_key = Inflector.camelize(raw_key)
Node.wrap(@raw[camel_key])
end
|
#child(key) ⇒ Object
Typed child access — returns the wrapped Node at ‘key`, or nil if absent or non-Node-shaped. Use this in lowering passes to avoid repetitive `is_a?(AST::Node)` defenses.
84
85
86
87
|
# File 'lib/jsx_rosetta/ast/node.rb', line 84
def child(key)
value = self[key]
value.is_a?(Node) ? value : nil
end
|
#children ⇒ Object
119
120
121
|
# File 'lib/jsx_rosetta/ast/node.rb', line 119
def children
each_child.to_a
end
|
#deconstruct_keys(keys) ⇒ Object
Pattern-matching support. Returns a hash with snake_case symbol keys; values are wrapped (Node instances or arrays of Node/raw values).
132
133
134
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/jsx_rosetta/ast/node.rb', line 132
def deconstruct_keys(keys)
if keys.nil?
@raw.each_with_object({}) do |(k, v), out|
out[Inflector.underscore(k).to_sym] = Node.wrap(v)
end
else
keys.each_with_object({}) do |key, out|
camel_key = Inflector.camelize(key.to_s)
actual_key = @raw.key?(key.to_s) ? key.to_s : camel_key
out[key] = Node.wrap(@raw[actual_key]) if @raw.key?(actual_key)
end
end
end
|
#dig(*keys) ⇒ Object
105
106
107
108
109
110
111
|
# File 'lib/jsx_rosetta/ast/node.rb', line 105
def dig(*keys)
keys.reduce(self) do |current, key|
break nil if current.nil?
current[key]
end
end
|
#each_child(&block) ⇒ Object
113
114
115
116
117
|
# File 'lib/jsx_rosetta/ast/node.rb', line 113
def each_child(&block)
return enum_for(:each_child) unless block
@raw.each_value { |value| yield_descendant_nodes(value, &block) }
end
|
#end_pos ⇒ Object
64
65
66
|
# File 'lib/jsx_rosetta/ast/node.rb', line 64
def end_pos
@raw["end"]
end
|
#hash ⇒ Object
151
152
153
|
# File 'lib/jsx_rosetta/ast/node.rb', line 151
def hash
@raw.hash
end
|
#inspect ⇒ Object
155
156
157
|
# File 'lib/jsx_rosetta/ast/node.rb', line 155
def inspect
"#<#{self.class.name || "JsxRosetta::AST::Node"} type=#{type.inspect} loc=#{loc_summary}>"
end
|
#loc ⇒ Object
52
53
54
|
# File 'lib/jsx_rosetta/ast/node.rb', line 52
def loc
@raw["loc"]
end
|
#of_type?(*types) ⇒ Boolean
Type predicate on a known Node. Use only when the receiver is guaranteed to be a Node — otherwise prefer ‘Node.matches?` (class method) which tolerates nil / non-Node values from hash lookups. Accepts multiple types: `node.of_type?(“StringLiteral”, “NumericLiteral”)`.
93
94
95
|
# File 'lib/jsx_rosetta/ast/node.rb', line 93
def of_type?(*types)
types.include?(type)
end
|
#range ⇒ Object
56
57
58
|
# File 'lib/jsx_rosetta/ast/node.rb', line 56
def range
@raw["range"]
end
|
#start_pos ⇒ Object
60
61
62
|
# File 'lib/jsx_rosetta/ast/node.rb', line 60
def start_pos
@raw["start"]
end
|
#type ⇒ Object
48
49
50
|
# File 'lib/jsx_rosetta/ast/node.rb', line 48
def type
@raw["type"]
end
|
#walk {|_self| ... } ⇒ Object
123
124
125
126
127
128
|
# File 'lib/jsx_rosetta/ast/node.rb', line 123
def walk(&block)
return enum_for(:walk) unless block
yield self
each_child { |child| child.walk(&block) }
end
|