Class: RuboCop::Cop::Glyphs::IconResolution

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Defined in:
lib/rubocop/cop/glyphs/icon_resolution.rb

Overview

Validates that statically-known icon names exist in the synced SVG directories (IconsPath), suggests close matches for typos, and flags raw iconify class strings.

Both Glyphs component calls (LucideIcon(:house)) and legacy helper calls (_lucide(:house)) are validated. A literal variant: keyword is honored; calls with dynamic names or variants are skipped.

Examples:

# bad
LucideIcon(:non_existent_icon)
span(class: "iconify lucide--house size-4")

# good
LucideIcon(:house, class: "size-4")

Constant Summary collapse

DEFAULT_LIBRARIES =
{
  "LucideIcon" => { "Dir" => "lucide", "DefaultVariant" => "outline" },
  "PhosphorIcon" => { "Dir" => "phosphor", "DefaultVariant" => "regular" },
  "HeroIcon" => { "Dir" => "heroicons", "DefaultVariant" => "outline" },
  "TablerIcon" => { "Dir" => "tabler", "DefaultVariant" => "outline" },
  "SidekickIcon" => { "Dir" => "sidekickicons", "DefaultVariant" => "outline" }
}.freeze
LEGACY_HELPERS =
{
  "_lucide" => "LucideIcon",
  "_phosphor" => "PhosphorIcon",
  "_hero" => "HeroIcon",
  "_heroicon" => "HeroIcon",
  "_tabler" => "TablerIcon"
}.freeze
ICONIFY_PREFIX_TO_COMPONENT =
{
  "lucide" => "LucideIcon",
  "phosphor" => "PhosphorIcon",
  "heroicons" => "HeroIcon"
}.freeze
KNOWN_LUCIDE_RENAMES =
{
  "alert-triangle" => "triangle-alert",
  "alert-circle" => "circle-alert",
  "x-circle" => "circle-x"
}.freeze
ICONIFY_PATTERN =
/\biconify\s+(lucide|phosphor|heroicons)--([a-z0-9-]+)/
RAW_MSG =
"Use `%{component}(:%{symbol}, class: \"%{rest}\")` instead of raw `iconify %{library}--…` class."
RAW_DSTR_MSG =
"Use `LucideIcon(name, class: ...)` etc. instead of building a raw " \
"`iconify <library>--…` class string."
MISSING_MSG =
"Icon `%{name}` not found in %{library}/%{variant}. %{suggestion}"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.available_icons_for(base_path, library_dir, variant) ⇒ Object



269
270
271
272
# File 'lib/rubocop/cop/glyphs/icon_resolution.rb', line 269

def available_icons_for(base_path, library_dir, variant)
  @available_icons_cache ||= {}
  @available_icons_cache[[base_path, library_dir, variant]] ||= load_icons(base_path, library_dir, variant)
end

Instance Method Details

#on_dstr(node) ⇒ Object



65
66
67
68
69
70
# File 'lib/rubocop/cop/glyphs/icon_resolution.rb', line 65

def on_dstr(node)
  combined = node.children.filter_map { |child| child.str_type? ? child.value : "INTERPOLATED" }.join
  return unless combined.include?("iconify ")

  add_offense(node, message: RAW_DSTR_MSG)
end

#on_send(node) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/rubocop/cop/glyphs/icon_resolution.rb', line 72

def on_send(node)
  return unless node.receiver.nil?

  component = component_for(node.method_name)
  return unless component

  library = libraries[component]
  return unless library

  check_call(node, component, library)
rescue StandardError => e
  source = node.location.expression.source_buffer.name
  warn "[Glyphs/IconResolution] suppressed #{e.class}: #{e.message} at #{source}:#{node.first_line}"
end

#on_str(node) ⇒ Object



59
60
61
62
63
# File 'lib/rubocop/cop/glyphs/icon_resolution.rb', line 59

def on_str(node)
  return if node.parent&.dstr_type?

  check_iconify_string(node, node.value)
end