Class: Rails::CssUnused::ViewScanner
- Inherits:
-
Object
- Object
- Rails::CssUnused::ViewScanner
- Defined in:
- lib/rails/css_unused/view_scanner.rb
Overview
Scans Rails view templates, ViewComponent files, Phlex components, Stimulus controllers, and Ruby files for referenced CSS class names.
Handles:
ERB: class="foo bar", class: "foo", class: ["foo", "bar"]
HAML: .foo.bar, %div.foo
Slim: div.foo, .foo
Ruby: html_class: "foo", css_classes("foo bar"), "foo bar"
Stimulus: this.element.classList.add("foo"), "foo" string literals
Dynamic: class="<%= cond ? 'foo' : 'bar' %>" — literal parts extracted
Dynamic vars: status_class = "foo-bar" — string assigned to *_class/*_classes var
Constant Summary collapse
- HTML_CLASS_ATTR =
── ERB / HTML patterns ──────────────────────────────────────────────class=“foo bar baz” or class=‘foo bar’
/class\s*=\s*["']([^"'<>]+)["']/i- RUBY_CLASS_KV =
class: “foo bar” or class: ‘foo’
/class:\s*["']([^"']+)["']/- RUBY_CLASS_ARRAY =
class: [“foo”, “bar”] or class: %w[foo bar]
/class:\s*(?:\[|%w\[)\s*([^\]\n]+)/- TAG_HELPER =
tag.div(class: “foo”) content_tag(:div, class: “foo”)
/(?:content_tag|tag\.\w+)\s*[({][^)}\n]*class:\s*["']([^"']+)["']/- HAML_IMPLICIT =
── HAML patterns ───────────────────────────────────────────────────.foo, %div.foo.bar, %span.foo#id
/^[ \t]*(?:%[\w:-]+)?(\.[a-zA-Z][a-zA-Z0-9_-]*(?:\.[a-zA-Z][a-zA-Z0-9_-]*)*)/- HAML_HASH_CLASS =
Inline { class: “foo” }
/class:\s*["']([^"']+)["']/- SLIM_CLASS =
── Slim patterns ───────────────────────────────────────────────────div.foo.bar or .foo.bar on its own line
/^[ \t]*(?:[\w-]*)(\.[a-zA-Z][a-zA-Z0-9_-]*(?:\.[a-zA-Z][a-zA-Z0-9_-]*)*)/- ERB_DYNAMIC_CLASS =
── ERB dynamic interpolation ────────────────────────────────────────class=“<%= expr %>”, class=“prefix-<%= var %>” — extracts static parts
/class\s*=\s*["'][^"']*<%=[^%]+%>[^"']*["']/m- DYNAMIC_CLASS_VAR =
── Dynamic class variable detection (v0.2.1) ────────────────────────Detects string literals assigned to variables whose name ends with _class, _classes, _style, or _css — and the string contains hyphens (Ruby variable names cannot contain hyphens, so it must be a value).
Matches patterns like:
status_class = "foo-bar" button_classes = "btn btn-primary" ["Active", "status-active"] (array element with hyphenated string) ["Cancelled", "status-cancelled"]Rule: any double- or single-quoted string containing at least one hyphen is unambiguously a string value (not a Ruby identifier), so we can safely extract it as a potential class name.
Pattern 1: variable ending in _class/_classes/_style/_css = “value”
/\b\w+_(?:class(?:es)?|style|css)\s*=\s*["']([^"'\n]+)["']/- HYPHENATED_STRING =
Pattern 2: any quoted string with hyphens in array/tuple context e.g. [“Active”, “status-active”] — the hyphenated strings are CSS classes
/["']([a-zA-Z][a-zA-Z0-9]*(?:-[a-zA-Z0-9]+)+)["']/- JS_ADD_CLASS =
── Ruby / Stimulus string literals ─────────────────────────────────Any double-quoted string that looks like a space-separated class list
/(?:classList\.add|classList\.toggle|classList\.replace)\s*\(\s*["']([^"']+)["']/- JS_REMOVE_CLASS =
/(?:classList\.remove)\s*\(\s*["']([^"']+)["']/- RUBY_STRING_CLASSES =
/["']([a-zA-Z][a-zA-Z0-9_-]*(?:\s+[a-zA-Z][a-zA-Z0-9_-]*)*)[\"']/
Instance Method Summary collapse
-
#initialize(root:, config: CssUnused.configuration) ⇒ ViewScanner
constructor
A new instance of ViewScanner.
-
#used_classes ⇒ Object
Returns a Set of class name strings referenced across all view files.
Constructor Details
#initialize(root:, config: CssUnused.configuration) ⇒ ViewScanner
Returns a new instance of ViewScanner.
69 70 71 72 |
# File 'lib/rails/css_unused/view_scanner.rb', line 69 def initialize(root:, config: CssUnused.configuration) @root = Pathname(root) @config = config end |
Instance Method Details
#used_classes ⇒ Object
Returns a Set of class name strings referenced across all view files.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rails/css_unused/view_scanner.rb', line 75 def used_classes classes = Set.new ignore_set = @config.ignore_classes.map(&:to_s).to_set ignore_pats = Array(@config.ignore_patterns) each_view_file do |path, content| extract_from(content, path).each do |cls| next if ignore_set.include?(cls) next if ignore_pats.any? { |p| cls.match?(p) } classes << cls end end if @config.scan_javascript_for_classes each_js_file do |path, content| extract_js_classes(content).each { |cls| classes << cls } end end classes end |