rails-css_unused 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -24
- data/LICENSE.txt +21 -21
- data/README.md +132 -132
- data/lib/rails/css_unused/configuration.rb +97 -45
- data/lib/rails/css_unused/railtie.rb +25 -24
- data/lib/rails/css_unused/report.rb +111 -50
- data/lib/rails/css_unused/spinner.rb +80 -0
- data/lib/rails/css_unused/stylesheet_scanner.rb +149 -82
- data/lib/rails/css_unused/tasks.rake +32 -12
- data/lib/rails/css_unused/version.rb +7 -7
- data/lib/rails/css_unused/view_scanner.rb +269 -117
- data/lib/rails/css_unused.rb +40 -36
- data/lib/rails-css_unused.rb +6 -3
- metadata +9 -6
|
@@ -1,117 +1,269 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rails
|
|
4
|
-
module CssUnused
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rails
|
|
4
|
+
module CssUnused
|
|
5
|
+
# Scans Rails view templates, ViewComponent files, Phlex components,
|
|
6
|
+
# Stimulus controllers, and Ruby files for referenced CSS class names.
|
|
7
|
+
#
|
|
8
|
+
# Handles:
|
|
9
|
+
# ERB: class="foo bar", class: "foo", class: ["foo", "bar"]
|
|
10
|
+
# HAML: .foo.bar, %div.foo
|
|
11
|
+
# Slim: div.foo, .foo
|
|
12
|
+
# Ruby: html_class: "foo", css_classes("foo bar"), "foo bar"
|
|
13
|
+
# Stimulus: this.element.classList.add("foo"), "foo" string literals
|
|
14
|
+
# Dynamic: class="<%= cond ? 'foo' : 'bar' %>" — literal parts extracted
|
|
15
|
+
class ViewScanner
|
|
16
|
+
# ── ERB / HTML patterns ──────────────────────────────────────────────
|
|
17
|
+
# class="foo bar baz" or class='foo bar'
|
|
18
|
+
HTML_CLASS_ATTR = /class\s*=\s*["']([^"'<>]+)["']/i
|
|
19
|
+
# class: "foo bar" or class: 'foo'
|
|
20
|
+
RUBY_CLASS_KV = /class:\s*["']([^"']+)["']/
|
|
21
|
+
# class: ["foo", "bar"] or class: %w[foo bar]
|
|
22
|
+
RUBY_CLASS_ARRAY = /class:\s*(?:\[|%w\[)\s*([^\]\n]+)/
|
|
23
|
+
# tag.div(class: "foo") content_tag(:div, class: "foo")
|
|
24
|
+
TAG_HELPER = /(?:content_tag|tag\.\w+)\s*[({][^)}\n]*class:\s*["']([^"']+)["']/
|
|
25
|
+
|
|
26
|
+
# ── HAML patterns ───────────────────────────────────────────────────
|
|
27
|
+
# .foo, %div.foo.bar, %span.foo#id
|
|
28
|
+
HAML_IMPLICIT = /^[ \t]*(?:%[\w:-]+)?(\.[a-zA-Z][a-zA-Z0-9_-]*(?:\.[a-zA-Z][a-zA-Z0-9_-]*)*)/
|
|
29
|
+
# Inline { class: "foo" }
|
|
30
|
+
HAML_HASH_CLASS = /class:\s*["']([^"']+)["']/
|
|
31
|
+
|
|
32
|
+
# ── Slim patterns ───────────────────────────────────────────────────
|
|
33
|
+
# div.foo.bar or .foo.bar on its own line
|
|
34
|
+
SLIM_CLASS = /^[ \t]*(?:[\w-]*)(\.[a-zA-Z][a-zA-Z0-9_-]*(?:\.[a-zA-Z][a-zA-Z0-9_-]*)*)/
|
|
35
|
+
|
|
36
|
+
# ── ERB dynamic interpolation ────────────────────────────────────────
|
|
37
|
+
# class="<%= expr %>", class="prefix-<%= var %>" — extracts static parts
|
|
38
|
+
ERB_DYNAMIC_CLASS = /class\s*=\s*["'][^"']*<%=[^%]+%>[^"']*["']/m
|
|
39
|
+
|
|
40
|
+
# ── Ruby / Stimulus string literals ─────────────────────────────────
|
|
41
|
+
# Any double-quoted string that looks like a space-separated class list
|
|
42
|
+
# Used when scan_javascript_for_classes or scan_ruby_components is on.
|
|
43
|
+
JS_ADD_CLASS = /(?:classList\.add|classList\.toggle|classList\.replace)\s*\(\s*["']([^"']+)["']/
|
|
44
|
+
JS_REMOVE_CLASS = /(?:classList\.remove)\s*\(\s*["']([^"']+)["']/ # these ARE used
|
|
45
|
+
RUBY_STRING_CLASSES = /["']([a-zA-Z][a-zA-Z0-9_-]*(?:\s+[a-zA-Z][a-zA-Z0-9_-]*)*)["']/
|
|
46
|
+
|
|
47
|
+
def initialize(root:, config: CssUnused.configuration)
|
|
48
|
+
@root = Pathname(root)
|
|
49
|
+
@config = config
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns a Set of class name strings referenced across all view files.
|
|
53
|
+
def used_classes
|
|
54
|
+
classes = Set.new
|
|
55
|
+
ignore_set = @config.ignore_classes.map(&:to_s).to_set
|
|
56
|
+
ignore_pats = Array(@config.ignore_patterns)
|
|
57
|
+
|
|
58
|
+
each_view_file do |path, content|
|
|
59
|
+
extract_from(content, path).each do |cls|
|
|
60
|
+
next if ignore_set.include?(cls)
|
|
61
|
+
next if ignore_pats.any? { |p| cls.match?(p) }
|
|
62
|
+
classes << cls
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if @config.scan_javascript_for_classes
|
|
67
|
+
each_js_file do |path, content|
|
|
68
|
+
extract_js_classes(content).each { |cls| classes << cls }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
classes
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
# ── File iteration ───────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
def each_view_file
|
|
80
|
+
paths = (@config.view_paths + @config.component_paths).uniq
|
|
81
|
+
paths.each do |rel|
|
|
82
|
+
dir = @root.join(rel)
|
|
83
|
+
next unless dir.directory?
|
|
84
|
+
|
|
85
|
+
Dir.glob(dir.join("**", "*")).each do |f|
|
|
86
|
+
path = Pathname(f)
|
|
87
|
+
next unless path.file? && view_file?(path)
|
|
88
|
+
|
|
89
|
+
content = safe_read(path)
|
|
90
|
+
yield path, content if content
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def each_js_file
|
|
96
|
+
@config.javascript_paths.each do |rel|
|
|
97
|
+
dir = @root.join(rel)
|
|
98
|
+
next unless dir.directory?
|
|
99
|
+
|
|
100
|
+
Dir.glob(dir.join("**", "*.{js,ts,jsx,tsx}")).each do |f|
|
|
101
|
+
path = Pathname(f)
|
|
102
|
+
content = safe_read(path)
|
|
103
|
+
yield path, content if content
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Also scan Ruby component files if configured
|
|
108
|
+
return unless @config.scan_ruby_components
|
|
109
|
+
|
|
110
|
+
@config.component_paths.each do |rel|
|
|
111
|
+
dir = @root.join(rel)
|
|
112
|
+
next unless dir.directory?
|
|
113
|
+
|
|
114
|
+
Dir.glob(dir.join("**", "*.rb")).each do |f|
|
|
115
|
+
path = Pathname(f)
|
|
116
|
+
content = safe_read(path)
|
|
117
|
+
yield path, content if content
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def view_file?(path)
|
|
123
|
+
name = path.basename.to_s
|
|
124
|
+
Configuration::VIEW_EXTENSIONS.any? { |ext| name.end_with?(ext) } ||
|
|
125
|
+
Configuration::COMPOUND_VIEW_ENDINGS.any? { |ext| name.end_with?(ext) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# ── Class extraction ─────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
def extract_from(content, path)
|
|
131
|
+
found = Set.new
|
|
132
|
+
ext = path.extname
|
|
133
|
+
base = path.basename.to_s
|
|
134
|
+
|
|
135
|
+
# ERB / HTML (also used for .html.erb compound names)
|
|
136
|
+
if ext == ".erb" || base.end_with?(".html.erb")
|
|
137
|
+
found.merge extract_erb(content)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# HAML
|
|
141
|
+
if ext == ".haml" || base.end_with?(".html.haml")
|
|
142
|
+
found.merge extract_haml(content)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Slim
|
|
146
|
+
if ext == ".slim" || base.end_with?(".html.slim")
|
|
147
|
+
found.merge extract_slim(content)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Ruby files (ViewComponent .rb, Phlex, helpers)
|
|
151
|
+
if ext == ".rb"
|
|
152
|
+
found.merge extract_ruby(content)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
found
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def extract_erb(content)
|
|
159
|
+
found = Set.new
|
|
160
|
+
|
|
161
|
+
# Standard class attributes
|
|
162
|
+
[HTML_CLASS_ATTR, RUBY_CLASS_KV, TAG_HELPER].each do |pat|
|
|
163
|
+
content.scan(pat) { |m| found.merge tokenize(m[0]) }
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# class: ["foo", "bar"] / class: %w[foo bar]
|
|
167
|
+
content.scan(RUBY_CLASS_ARRAY) do |m|
|
|
168
|
+
found.merge tokenize(m[0].gsub(/["',]/, " "))
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# ERB dynamic class attributes — extract static string parts
|
|
172
|
+
content.scan(ERB_DYNAMIC_CLASS) do
|
|
173
|
+
chunk = Regexp.last_match(0)
|
|
174
|
+
# Strip ERB tags, grab remaining quoted tokens
|
|
175
|
+
stripped = chunk.gsub(/<%.*?%>/m, " ")
|
|
176
|
+
stripped.scan(/["']([^"']+)["']/) { |m| found.merge tokenize(m[0]) }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
found
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def extract_haml(content)
|
|
183
|
+
found = Set.new
|
|
184
|
+
|
|
185
|
+
content.scan(HAML_IMPLICIT) do |m|
|
|
186
|
+
# m[0] = ".foo.bar" — split on dots
|
|
187
|
+
m[0].split(".").reject(&:empty?).each do |cls|
|
|
188
|
+
found << cls if valid_class?(cls)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
content.scan(HAML_HASH_CLASS) { |m| found.merge tokenize(m[0]) }
|
|
193
|
+
content.scan(RUBY_CLASS_KV) { |m| found.merge tokenize(m[0]) }
|
|
194
|
+
|
|
195
|
+
found
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def extract_slim(content)
|
|
199
|
+
found = Set.new
|
|
200
|
+
|
|
201
|
+
content.scan(SLIM_CLASS) do |m|
|
|
202
|
+
m[0].split(".").reject(&:empty?).each do |cls|
|
|
203
|
+
found << cls if valid_class?(cls)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
content.scan(RUBY_CLASS_KV) { |m| found.merge tokenize(m[0]) }
|
|
208
|
+
|
|
209
|
+
found
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def extract_ruby(content)
|
|
213
|
+
found = Set.new
|
|
214
|
+
|
|
215
|
+
# ViewComponent / Phlex: render with class: "foo"
|
|
216
|
+
content.scan(RUBY_CLASS_KV) { |m| found.merge tokenize(m[0]) }
|
|
217
|
+
content.scan(RUBY_CLASS_ARRAY) { |m| found.merge tokenize(m[0].gsub(/["',]/, " ")) }
|
|
218
|
+
|
|
219
|
+
# Loose string literals that look like class name lists (safe: validated below)
|
|
220
|
+
content.scan(RUBY_STRING_CLASSES) do |m|
|
|
221
|
+
tokens = tokenize(m[0])
|
|
222
|
+
# Only trust them if every token is a plausible CSS class
|
|
223
|
+
found.merge(tokens) if tokens.all? { |t| valid_class?(t) }
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
found
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def extract_js_classes(content)
|
|
230
|
+
found = Set.new
|
|
231
|
+
content.scan(JS_ADD_CLASS) { |m| found.merge tokenize(m[0]) }
|
|
232
|
+
content.scan(JS_REMOVE_CLASS) { |m| found.merge tokenize(m[0]) }
|
|
233
|
+
found
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# ── Helpers ───────────────────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
# Splits a raw class attribute value into individual class tokens.
|
|
239
|
+
# Handles: spaces, commas, quotes, ERB fragments.
|
|
240
|
+
def tokenize(raw)
|
|
241
|
+
return Set.new if raw.nil?
|
|
242
|
+
|
|
243
|
+
raw
|
|
244
|
+
.gsub(/["']/, " ") # strip stray quotes
|
|
245
|
+
.split(/[\s,]+/) # split on whitespace/commas
|
|
246
|
+
.map { |t| t.strip.delete_prefix(".") }
|
|
247
|
+
.reject(&:empty?)
|
|
248
|
+
.reject { |t| t.include?("<%") || t.include?('#{') }
|
|
249
|
+
.select { |t| valid_class?(t) }
|
|
250
|
+
.to_set
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# A valid CSS class token: starts with a letter or underscore,
|
|
254
|
+
# contains only alphanumeric, hyphens, underscores.
|
|
255
|
+
# Allows BEM: block__element--modifier
|
|
256
|
+
def valid_class?(token)
|
|
257
|
+
token.match?(/\A-?[a-zA-Z_][a-zA-Z0-9_-]*\z/)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def safe_read(path)
|
|
261
|
+
path.read(encoding: "UTF-8")
|
|
262
|
+
rescue ArgumentError, Encoding::UndefinedConversionError
|
|
263
|
+
path.read(encoding: "BINARY").encode("UTF-8", invalid: :replace, undef: :replace)
|
|
264
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
265
|
+
nil
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
data/lib/rails/css_unused.rb
CHANGED
|
@@ -1,36 +1,40 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "pathname"
|
|
4
|
-
require "set"
|
|
5
|
-
|
|
6
|
-
require_relative "css_unused/version"
|
|
7
|
-
require_relative "css_unused/configuration"
|
|
8
|
-
require_relative "css_unused/
|
|
9
|
-
require_relative "css_unused/
|
|
10
|
-
require_relative "css_unused/report"
|
|
11
|
-
|
|
12
|
-
module Rails
|
|
13
|
-
module CssUnused
|
|
14
|
-
class Error < StandardError; end
|
|
15
|
-
|
|
16
|
-
class << self
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "set"
|
|
5
|
+
|
|
6
|
+
require_relative "css_unused/version"
|
|
7
|
+
require_relative "css_unused/configuration"
|
|
8
|
+
require_relative "css_unused/stylesheet_scanner"
|
|
9
|
+
require_relative "css_unused/view_scanner"
|
|
10
|
+
require_relative "css_unused/report"
|
|
11
|
+
|
|
12
|
+
module Rails
|
|
13
|
+
module CssUnused
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
# Prints a full report to output (default: $stdout).
|
|
18
|
+
# Returns the exit code (0 = clean, 1 = ghosts found + fail_on_unused).
|
|
19
|
+
def report(root: default_root, output: $stdout)
|
|
20
|
+
Report.new(root: root, output: output).print_summary
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns an array of unused class name strings.
|
|
24
|
+
def ghost_classes(root: default_root)
|
|
25
|
+
Report.new(root: root).ghost_classes.map(&:class_name)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns the project root: Rails.root if inside Rails, else cwd.
|
|
29
|
+
def default_root
|
|
30
|
+
if defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root
|
|
31
|
+
::Rails.root
|
|
32
|
+
else
|
|
33
|
+
Pathname.new(Dir.pwd)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
require_relative "css_unused/railtie" if defined?(Rails::Railtie)
|
data/lib/rails-css_unused.rb
CHANGED
metadata
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-css_unused
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sghani001
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2026-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
@@ -59,9 +59,11 @@ dependencies:
|
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
60
|
version: '3.12'
|
|
61
61
|
description: |
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
rails-css_unused scans your stylesheets (CSS/SCSS/Sass) and all view templates
|
|
63
|
+
(ERB, HAML, Slim), ViewComponents, Phlex components, and Stimulus JS controllers
|
|
64
|
+
to report CSS class selectors that are defined but never referenced. Supports
|
|
65
|
+
BEM naming, dynamic class detection, source-file attribution, CI exit codes,
|
|
66
|
+
and configurable ignore lists — zero runtime overhead.
|
|
65
67
|
email:
|
|
66
68
|
- sghani001@users.noreply.github.com
|
|
67
69
|
executables: []
|
|
@@ -76,6 +78,7 @@ files:
|
|
|
76
78
|
- lib/rails/css_unused/configuration.rb
|
|
77
79
|
- lib/rails/css_unused/railtie.rb
|
|
78
80
|
- lib/rails/css_unused/report.rb
|
|
81
|
+
- lib/rails/css_unused/spinner.rb
|
|
79
82
|
- lib/rails/css_unused/stylesheet_scanner.rb
|
|
80
83
|
- lib/rails/css_unused/tasks.rake
|
|
81
84
|
- lib/rails/css_unused/version.rb
|
|
@@ -107,5 +110,5 @@ requirements: []
|
|
|
107
110
|
rubygems_version: 3.5.3
|
|
108
111
|
signing_key:
|
|
109
112
|
specification_version: 4
|
|
110
|
-
summary: Find unused CSS classes in Rails apps
|
|
113
|
+
summary: Find unused CSS classes in Rails apps — fast, accurate static analysis
|
|
111
114
|
test_files: []
|