Class: Guardrails::Icons
- Inherits:
-
Object
- Object
- Guardrails::Icons
- Defined in:
- lib/guardrails/icons.rb
Defined Under Namespace
Classes: Violation
Constant Summary collapse
- DEFAULT_SOURCE =
"app/assets/images/icons"- DEFAULT_SPRITE_OUTPUT =
"app/assets/images/icons/sprite.svg"- DEFAULT_VIEWBOX =
"0 0 24 24"- VIEW_PATTERNS =
[ "app/views/**/*.html.erb", "app/components/**/*.html.erb" ].freeze
- SVG_OPEN_TAG =
/<svg\b([^>]*)>/m- SVG_INNER =
/<svg\b[^>]*>([\s\S]*?)<\/svg>/m- SVG_BLOCK_PATTERN =
/<svg\b[^>]*>[\s\S]*?<\/svg>/m- VIEWBOX_ATTR =
/\bviewBox\s*=\s*["']([^"']+)["']/i- ERB_BLOCK_PATTERN =
/<%[\s\S]*?%>/- USAGE_PATTERNS =
Patterns that indicate an icon is in use. We’re conservative on this side — false negatives (saying “alive” when actually dead) just leave dead icons in source; false positives (saying “dead” when actually used) cause the user to delete files they need.
Each pattern allows an optional directory prefix before the basename (e.g. ‘image_tag “icons/check.svg”` for files under `app/assets/images/icons/`) and captures only the bare name so it matches against icons collected from disk.
[ # Sprite reference: <use href="#icon-foo"> /#icon-([\w-]+)/, # Rails image_tag "foo.svg" / image_tag "icons/foo.svg" /\bimage_tag\s*\(?\s*["'](?:[^"']*\/)?([\w-]+)\.(?:svg|png|gif|jpe?g|webp)["']/, # Rails asset_path / asset_url / image_path / image_url with optional path /\b(?:asset_path|asset_url|image_path|image_url)\s*\(?\s*["'](?:[^"']*\/)?([\w-]+)\.(?:svg|png|gif|jpe?g|webp)["']/, # CSS url() references in stylesheets and inline style attributes /url\s*\(\s*["']?(?:[^"')]*\/)?([\w-]+)\.(?:svg|png|gif|jpe?g|webp)/i ].freeze
- USAGE_SCAN_PATTERNS =
[ "app/views/**/*.html.erb", "app/components/**/*.html.erb", "app/components/**/*.rb", "app/assets/stylesheets/**/*.{css,scss,sass}", "app/javascript/**/*.{js,ts,jsx,tsx}" ].freeze
Instance Method Summary collapse
- #audit_inline_svgs ⇒ Object
- #generate_sprite ⇒ Object
-
#initialize(root:, output: $stdout, source: nil, sprite_output: nil) ⇒ Icons
constructor
A new instance of Icons.
- #report_dead_icons ⇒ Object
- #run ⇒ Object
Constructor Details
#initialize(root:, output: $stdout, source: nil, sprite_output: nil) ⇒ Icons
Returns a new instance of Icons.
53 54 55 56 57 58 59 60 |
# File 'lib/guardrails/icons.rb', line 53 def initialize(root:, output: $stdout, source: nil, sprite_output: nil) @root = Pathname(root) @output = output config = load_config @source = resolve_path(source || config.dig("guardrails", "icons", "source") || DEFAULT_SOURCE) @sprite_output = resolve_path(sprite_output || config.dig("guardrails", "icons", "sprite_output") || DEFAULT_SPRITE_OUTPUT) end |
Instance Method Details
#audit_inline_svgs ⇒ Object
71 72 73 |
# File 'lib/guardrails/icons.rb', line 71 def audit_inline_svgs view_files.flat_map { |file| scan_view_for_inline_svgs(file) } end |
#generate_sprite ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/guardrails/icons.rb', line 84 def generate_sprite svgs = collect_svgs if svgs.empty? @output.puts "No SVGs found in #{relative(@source)}" return nil end symbols = svgs.filter_map { |file| build_symbol(file) } sprite = wrap_sprite(symbols) @sprite_output.dirname.mkpath File.write(@sprite_output, sprite, encoding: Encoding::UTF_8) @output.puts "Wrote sprite with #{symbols.length} icons to #{relative(@sprite_output)}" @sprite_output end |
#report_dead_icons ⇒ Object
75 76 77 78 79 80 81 82 |
# File 'lib/guardrails/icons.rb', line 75 def report_dead_icons icon_names = collect_icon_names used_names = collect_used_icon_names { dead: (icon_names - used_names).sort, unknown: (used_names - icon_names).sort } end |
#run ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/guardrails/icons.rb', line 62 def run generate_sprite violations = audit_inline_svgs report_inline_svgs(violations) dead_report = report_dead_icons print_dead_report(dead_report) { inline_svgs: violations, dead_icons: dead_report[:dead], unknown_refs: dead_report[:unknown] } end |