class AstTraverser
:nodoc:
def component_name(node)
def component_name(node) return node.receiver.const_name if node.method_name == :new helper_key = node.method_name.to_s.gsub("ariadne_", "").to_sym Ariadne::ViewHelper::HELPERS[helper_key] end
def component_node?(node)
def component_node?(node) view_helpers.include?(node.method_name) || (node.method_name == :new && !node.receiver.nil? && ::Ariadne::ViewComponents::STATUSES.key?(node.receiver.const_name)) end
def extract_arguments(node, name)
def extract_arguments(node, name) args = node.arguments res = {} return res if args.empty? kwargs = args.last if kwargs.respond_to?(:pairs) res = kwargs.pairs.each_with_object({}) do |pair, h| h.merge!(extract_values(pair)) end end # Heroicon is the only component that accepts positional arguments. res[:icon] = args.first.source if name == "Ariadne::HeroiconComponent" && args.size > 1 res end
def extract_values(pair)
def extract_values(pair) return { pair.key.value => pair.value.source } unless pair.value.type == :hash flatten_pairs(pair, prefix: "#{pair.key.value}-") end
def flatten_pairs(pair, prefix: "")
def flatten_pairs(pair, prefix: "") pair.value.pairs.each_with_object({}) do |value_pair, h| if value_pair.value.type == :hash h.merge!(flatten_pairs(value_pair, prefix: "#{prefix}#{value_pair.key.value}-")) else h["#{prefix}#{value_pair.key.value}"] = value_pair.value.source end end end
def initialize
def initialize @stats = {} end
def on_send(node)
def on_send(node) return super(node) unless component_node?(node) name = component_name(node) args = extract_arguments(node, name) @stats[name] = { path: node.loc.expression.source_buffer.name } @stats[name][:arguments] = args unless args.empty? super(node) # recursively iterate over children end
def view_helpers
def view_helpers @view_helpers ||= ::Ariadne::ViewHelper::HELPERS.keys.map { |key| "ariadne_#{key}".to_sym } end