glyphs 0.2.3 → 0.2.4
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 +26 -0
- data/README.md +28 -0
- data/config/default.yml +13 -4
- data/lib/glyphs/version.rb +1 -1
- data/lib/rubocop/cop/glyphs/icon_resolution.rb +54 -6
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5635cb76266896b0fb2d2e661844d27400be25b6e4a1190271279c7a4331517c
|
|
4
|
+
data.tar.gz: d8afd8c82d444afd15e951eba3aae5d1daaa0fce73efd7ac86fe725b9ce6dd09
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dffab87c9e9862dad15042135dc4c2ef7fb5785dd848210538b6e63c5263b8be6fc4a9b6c5276ac8ee7523fe239e08bb63007aff45af8ba52721fd52d774a946
|
|
7
|
+
data.tar.gz: 492c537c6ebb87013ec9795406b78cfb6b50e06328e2cc9b8b072a0c359e71d575a6d4853450c66b7d3c526492679ad156ef858a122cf848f4856ae957b14e55
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@
|
|
|
4
4
|
|
|
5
5
|
### Fixed
|
|
6
6
|
|
|
7
|
+
- **`Glyphs/IconResolution` no longer fails open on a missing icon directory.**
|
|
8
|
+
A directory that did not exist produced the same empty list as one that
|
|
9
|
+
existed with no SVGs, and both were read as "nothing to check" — so a library
|
|
10
|
+
that was never synced, or a `DefaultVariant` naming a variant the project does
|
|
11
|
+
not have, silently disabled the cop for every call site of that library. Green
|
|
12
|
+
cop, green CI, no validation at all, indefinitely. `load_icons` now returns
|
|
13
|
+
`nil` for an absent directory and `[]` for an empty one; the absent case warns
|
|
14
|
+
once per library/variant, naming the path. The synced-but-empty case still
|
|
15
|
+
passes silently. Refs #8
|
|
16
|
+
|
|
17
|
+
- **Cop options are declared, so RuboCop stops calling them unsupported.**
|
|
18
|
+
`config/default.yml` documented `Libraries` (`Glyphs/IconResolution`) and
|
|
19
|
+
`Mappings` (`Glyphs/LegacyIconHelper`) only in comments, and never mentioned
|
|
20
|
+
`LibraryComponents` (`Glyphs/LegacyIconHelper`, `Glyphs/PreferLibraryComponent`)
|
|
21
|
+
at all. RuboCop derives its supported-parameter list from the keys actually
|
|
22
|
+
present in that file, so it warned `does not support <param> parameter` on
|
|
23
|
+
every run — for four working options, one of which
|
|
24
|
+
`Glyphs/LegacyIconHelper` itself tells you to add. Each is now declared with an
|
|
25
|
+
empty default (a no-op merge), so the options are validated instead of
|
|
26
|
+
reported, with behaviour unchanged. Refs #7
|
|
27
|
+
|
|
7
28
|
- **Declaration harvest unwraps trailing `.freeze`.** `ICONS = { "x" => :car }.freeze`
|
|
8
29
|
(and `%i[a b].freeze`) used to yield a Prism `CallNode`, so hash/array values
|
|
9
30
|
were never collected. Cross-file dynamics (`PhosphorIcon(@icon)` in a shared
|
|
@@ -13,6 +34,11 @@
|
|
|
13
34
|
|
|
14
35
|
### Added
|
|
15
36
|
|
|
37
|
+
- **`Glyphs/IconResolution` gained a `Strict` option** (default `false`). It
|
|
38
|
+
escalates the missing-icon-directory warning above to an offence, so projects
|
|
39
|
+
that depend on this cop can fail closed in CI instead of trusting a warning
|
|
40
|
+
not to scroll past.
|
|
41
|
+
|
|
16
42
|
- **Dynamic icon calls are now resolved from source.** `SourceScanner` no longer
|
|
17
43
|
silently skips `LucideIcon(some_var)` / `PhosphorIcon(tile[:icon])` — it harvests
|
|
18
44
|
the literal name from two places so the pruner keeps it:
|
data/README.md
CHANGED
|
@@ -210,6 +210,10 @@ Glyphs/LegacyIconHelper:
|
|
|
210
210
|
# Mappings replaces the built-in defaults (_lucide/_phosphor/_hero/_heroicon/_tabler):
|
|
211
211
|
# Mappings:
|
|
212
212
|
# _custom: CustomIcon
|
|
213
|
+
# LibraryComponents merges over the built-in library => component map, so
|
|
214
|
+
# `icon("x", library: "customlib")` corrects to `CustomIcon("x")`:
|
|
215
|
+
# LibraryComponents:
|
|
216
|
+
# customlib: CustomIcon
|
|
213
217
|
```
|
|
214
218
|
|
|
215
219
|
### Glyphs/IconResolution
|
|
@@ -231,14 +235,38 @@ Glyphs/IconResolution:
|
|
|
231
235
|
# Libraries merges over the built-in defaults (component => Dir/DefaultVariant):
|
|
232
236
|
Libraries:
|
|
233
237
|
PhosphorIcon: { Dir: phosphor, DefaultVariant: regular }
|
|
238
|
+
# Escalate a missing icon directory from a warning to an offence:
|
|
239
|
+
Strict: false
|
|
234
240
|
```
|
|
235
241
|
|
|
242
|
+
If the directory a library resolves to does not exist — the library was never synced, or
|
|
243
|
+
`DefaultVariant` names a variant you do not have — the cop cannot validate any of that
|
|
244
|
+
library's call sites. Rather than pass silently, it warns once per library/variant:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
[Glyphs/IconResolution] Icon directory `app/assets/svg/icons/phosphor/regular` not found,
|
|
248
|
+
so `PhosphorIcon` names are not validated. Sync the library or fix `IconsPath`/`Libraries`.
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Set `Strict: true` to make that an offence instead, so CI fails closed rather than reporting
|
|
252
|
+
green while checking nothing. A directory that exists but ships no SVGs is not a
|
|
253
|
+
misconfiguration and stays silent either way.
|
|
254
|
+
|
|
236
255
|
### Glyphs/PreferLibraryComponent
|
|
237
256
|
|
|
238
257
|
```ruby
|
|
239
258
|
Icon(:house, library: :lucide) # => LucideIcon(:house)
|
|
240
259
|
```
|
|
241
260
|
|
|
261
|
+
```yaml
|
|
262
|
+
Glyphs/PreferLibraryComponent:
|
|
263
|
+
Include:
|
|
264
|
+
- app/**/*.rb
|
|
265
|
+
# LibraryComponents merges over the built-in library => component map:
|
|
266
|
+
# LibraryComponents:
|
|
267
|
+
# customlib: CustomIcon
|
|
268
|
+
```
|
|
269
|
+
|
|
242
270
|
## Migrating an app off icon helpers
|
|
243
271
|
|
|
244
272
|
1. Add `gem "glyphs"`, `include Glyphs` where the helpers used to be included.
|
data/config/default.yml
CHANGED
|
@@ -3,10 +3,13 @@ Glyphs/LegacyIconHelper:
|
|
|
3
3
|
Enabled: true
|
|
4
4
|
VersionAdded: '0.1.0'
|
|
5
5
|
SafeAutoCorrect: true
|
|
6
|
-
#
|
|
7
|
-
# Mappings:
|
|
6
|
+
# Replaces the built-in helper => component defaults when set, e.g.
|
|
8
7
|
# _lucide: LucideIcon
|
|
9
8
|
# _heroicon: HeroIcon
|
|
9
|
+
Mappings: {}
|
|
10
|
+
# Merges over the built-in library => component map, e.g.
|
|
11
|
+
# customlib: CustomIcon
|
|
12
|
+
LibraryComponents: {}
|
|
10
13
|
DefaultLibraryComponent: HeroIcon
|
|
11
14
|
|
|
12
15
|
Glyphs/IconResolution:
|
|
@@ -14,12 +17,18 @@ Glyphs/IconResolution:
|
|
|
14
17
|
Enabled: true
|
|
15
18
|
VersionAdded: '0.1.0'
|
|
16
19
|
IconsPath: app/assets/svg/icons
|
|
17
|
-
#
|
|
18
|
-
# Libraries:
|
|
20
|
+
# Merges over the built-in component => directory/variant defaults, e.g.
|
|
19
21
|
# PhosphorIcon: { Dir: phosphor, DefaultVariant: light }
|
|
22
|
+
Libraries: {}
|
|
23
|
+
# An icon directory that does not exist warns once per library/variant.
|
|
24
|
+
# Strict turns that warning into an offence so CI fails closed.
|
|
25
|
+
Strict: false
|
|
20
26
|
|
|
21
27
|
Glyphs/PreferLibraryComponent:
|
|
22
28
|
Description: 'Prefer library-specific components over generic Icon(..., library: ...).'
|
|
23
29
|
Enabled: true
|
|
24
30
|
VersionAdded: '0.1.0'
|
|
25
31
|
SafeAutoCorrect: true
|
|
32
|
+
# Merges over the built-in library => component map, e.g.
|
|
33
|
+
# customlib: CustomIcon
|
|
34
|
+
LibraryComponents: {}
|
data/lib/glyphs/version.rb
CHANGED
|
@@ -11,6 +11,11 @@ module RuboCop
|
|
|
11
11
|
# calls (`_lucide(:house)`) are validated. A literal `variant:` keyword is
|
|
12
12
|
# honored; calls with dynamic names or variants are skipped.
|
|
13
13
|
#
|
|
14
|
+
# A directory that does not exist is a configuration error, not "nothing
|
|
15
|
+
# to check": it warns once per library/variant, or — with `Strict` — adds
|
|
16
|
+
# an offence, so the cop can never quietly validate nothing. A directory
|
|
17
|
+
# that exists but ships no SVGs stays silent.
|
|
18
|
+
#
|
|
14
19
|
# @example
|
|
15
20
|
# # bad
|
|
16
21
|
# LucideIcon(:non_existent_icon)
|
|
@@ -51,10 +56,14 @@ module RuboCop
|
|
|
51
56
|
|
|
52
57
|
ICONIFY_PATTERN = /\biconify\s+(lucide|phosphor|heroicons)--([a-z0-9-]+)/
|
|
53
58
|
|
|
59
|
+
DEFAULT_ICONS_PATH = "app/assets/svg/icons"
|
|
60
|
+
|
|
54
61
|
RAW_MSG = "Use `%{component}(:%{symbol}, class: \"%{rest}\")` instead of raw `iconify %{library}--…` class."
|
|
55
62
|
RAW_DSTR_MSG = "Use `LucideIcon(name, class: ...)` etc. instead of building a raw " \
|
|
56
63
|
"`iconify <library>--…` class string."
|
|
57
64
|
MISSING_MSG = "Icon `%{name}` not found in %{library}/%{variant}. %{suggestion}"
|
|
65
|
+
MISSING_DIR_MSG = "Icon directory `%{path}` not found, so `%{component}` names are not validated. " \
|
|
66
|
+
"Sync the library or fix `IconsPath`/`Libraries`."
|
|
58
67
|
|
|
59
68
|
def on_str(node)
|
|
60
69
|
return if node.parent&.dstr_type?
|
|
@@ -97,7 +106,7 @@ module RuboCop
|
|
|
97
106
|
@libraries ||= DEFAULT_LIBRARIES.merge(cop_config["Libraries"] || {})
|
|
98
107
|
end
|
|
99
108
|
|
|
100
|
-
def check_call(node,
|
|
109
|
+
def check_call(node, component, library)
|
|
101
110
|
name = literal_name(node.first_argument)
|
|
102
111
|
return unless name
|
|
103
112
|
|
|
@@ -105,6 +114,7 @@ module RuboCop
|
|
|
105
114
|
return if variant == :dynamic
|
|
106
115
|
|
|
107
116
|
available = available_icons(library["Dir"], variant)
|
|
117
|
+
return report_missing_directory(node, component, library["Dir"], variant) if available.nil?
|
|
108
118
|
return if available.empty?
|
|
109
119
|
return if available.include?(name)
|
|
110
120
|
|
|
@@ -124,6 +134,21 @@ module RuboCop
|
|
|
124
134
|
end
|
|
125
135
|
end
|
|
126
136
|
|
|
137
|
+
# The directory the cop was told to read is missing, so every call for
|
|
138
|
+
# this library goes unchecked. Report it rather than pass silently.
|
|
139
|
+
def report_missing_directory(node, component, library_dir, variant)
|
|
140
|
+
message = format(MISSING_DIR_MSG, path: configured_directory(library_dir, variant), component:)
|
|
141
|
+
return add_offense(node.first_argument, message:) if cop_config["Strict"]
|
|
142
|
+
|
|
143
|
+
self.class.warn_once("[Glyphs/IconResolution] #{message}")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Built from the unexpanded `IconsPath` so the message shows the path as
|
|
147
|
+
# the project wrote it, not an absolute machine-specific one.
|
|
148
|
+
def configured_directory(library_dir, variant)
|
|
149
|
+
self.class.directory_for(cop_config["IconsPath"] || DEFAULT_ICONS_PATH, library_dir, variant)
|
|
150
|
+
end
|
|
151
|
+
|
|
127
152
|
def variant_for(node, library)
|
|
128
153
|
pair = variant_pair(node)
|
|
129
154
|
return library["DefaultVariant"] if pair.nil?
|
|
@@ -258,7 +283,7 @@ module RuboCop
|
|
|
258
283
|
end
|
|
259
284
|
|
|
260
285
|
def icons_base_path
|
|
261
|
-
@icons_base_path ||= File.expand_path(cop_config["IconsPath"] ||
|
|
286
|
+
@icons_base_path ||= File.expand_path(cop_config["IconsPath"] || DEFAULT_ICONS_PATH, Dir.pwd)
|
|
262
287
|
end
|
|
263
288
|
|
|
264
289
|
def available_icons(library_dir, variant)
|
|
@@ -266,16 +291,39 @@ module RuboCop
|
|
|
266
291
|
end
|
|
267
292
|
|
|
268
293
|
class << self
|
|
294
|
+
# nil when the directory is absent, [] when it exists but ships no
|
|
295
|
+
# SVGs — the caller has to tell those apart. Memoized with `key?` (not
|
|
296
|
+
# `||=`) so a nil result is not re-probed on every call site.
|
|
269
297
|
def available_icons_for(base_path, library_dir, variant)
|
|
270
298
|
@available_icons_cache ||= {}
|
|
271
|
-
|
|
299
|
+
key = [base_path, library_dir, variant]
|
|
300
|
+
return @available_icons_cache[key] if @available_icons_cache.key?(key)
|
|
301
|
+
|
|
302
|
+
@available_icons_cache[key] = load_icons(directory_for(base_path, library_dir, variant))
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def directory_for(base_path, library_dir, variant)
|
|
306
|
+
File.join(*[base_path, library_dir, variant].compact.reject { |part| part.to_s.empty? })
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# RuboCop builds a fresh cop instance per file, so deduplicating the
|
|
310
|
+
# missing-directory warning has to outlive the instance.
|
|
311
|
+
def warn_once(message)
|
|
312
|
+
@warned_messages ||= {}
|
|
313
|
+
return if @warned_messages.key?(message)
|
|
314
|
+
|
|
315
|
+
@warned_messages[message] = true
|
|
316
|
+
warn message
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def reset_warnings!
|
|
320
|
+
@warned_messages = nil
|
|
272
321
|
end
|
|
273
322
|
|
|
274
323
|
private
|
|
275
324
|
|
|
276
|
-
def load_icons(
|
|
277
|
-
|
|
278
|
-
return [] unless Dir.exist?(path)
|
|
325
|
+
def load_icons(path)
|
|
326
|
+
return nil unless Dir.exist?(path)
|
|
279
327
|
|
|
280
328
|
Dir.children(path).filter_map { |file| File.basename(file, ".svg") if file.end_with?(".svg") }.sort
|
|
281
329
|
end
|