Skip to content

Commit e1e2b8d

Browse files
committed
refactor(lint): harden icon-color-variables against silent failures
Address PR review feedback on the icon-color-variables rule: - Merge variable name indices across ALL candidate files instead of requiring one file to hold every expected variable; binding validation always runs against the merged index, so a degraded/failed variable fetch or a renamed variable no longer silently skips the whole check. - Treat config mistakes (selector matching nothing, empty allow-list, no component matched, expected variables absent) as .warning, not a hard .error that fails CI. - Surface empty fileId with a precise message before resolution. - Sample components with prefix(50) to respect NodesEndpoint rate limits. - Skip fully transparent subtrees (opacity == 0); O(n) lookup -> dict. - Wrap the generated policy in IconColorPolicy (failable init rejects an empty allow-list, single allowed(for:) overlap rule); fix description to reflect that binding is opt-in via requireBound. Tests: - Add fileId-aware MockClient overloads (setResponse/setError for:fileId:) so cross-file resolution can be tested per file. - Cover all API-failure paths, empty-fileId, multiple/nested fills, transparent skip, RTL skip, unbound stroke, and unit-test variableLookupKeys + IconColorPolicy. Rule suite: 10 -> 26 tests. Clarify Lint.pkl field comments and capture session learnings in pkl-codegen / troubleshooting / lint-patterns rule files.
1 parent 07ed092 commit e1e2b8d

8 files changed

Lines changed: 758 additions & 159 deletions

File tree

‎.claude/rules/lint-patterns.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ Uses `FigmaAPI.Client.request(SomeEndpoint(...))` directly (no convenience metho
2727
- `NodesEndpoint` supports `geometry: .paths` parameter — returns `fillGeometry`/`strokeGeometry` with SVG path data on vector nodes. **Not suitable for pathData validation** — Figma's SVG export flattens masks/booleans into different paths than raw geometry
2828
- `PathDataLengthRule` checks ALL platform icon entries (iOS/Android/Flutter/Web), deduplicates by fileId+frame+page. Downloads SVGs via `ImageEndpoint` + `URLSession`, parses with `SVGParser`, validates with `PathDataValidator`. Only reports critical >32,767 byte errors (800-char threshold removed as too noisy). Groups by fileId, batches ImageEndpoint by 50, parallelizes SVG downloads (max 10 concurrent) and fileIds
2929
- `InvalidRTLVariantValueRule` validates RTL variant property values against configured `rtlActiveValues` (default `["On"]`) and their known counterpart pairs (On↔Off, true↔false, True↔False, Yes↔No, 0↔1). Uses Components API only (no ImageEndpoint). Collects icon entries from all platforms with `rtlProperty` and `rtlActiveValues`, deduplicates by fileId+frame+page+rtlProperty. `validateRTLValues` and `validValues(for:)` are internal for testability. Suggests either renaming in Figma or adding value to `rtlActiveValues` config
30+
- `MockClient` keys responses by endpoint TYPE by default — multi-file rule tests need the fileId-aware overloads `setResponse(_:for:fileId:)` / `setError(_:for:fileId:)` (file-specific takes precedence over file-agnostic; fileId extracted from the `…/v1/files/{fileId}/…` request path)
31+
- `IconColorVariablesLintRule` resolves expected variables by MERGING name indices from ALL candidate files (not requiring one file to hold all) — expected-but-absent variables emit `.warning` (config drift) while binding validation still runs against the merged index. Selector / empty-allow-list / no-component-match mismatches are `.warning` (config mistakes), not `.error`. `IconColorPolicy` (failable init rejects empty allow-list) and `PaintRole` are internal for testability; `variableLookupKeys` parses `VariableID:` prefix + `library-key/localId` cross-file refs. Samples `prefix(50)` like `DarkModeVariablesRule`

‎.claude/rules/pkl-codegen.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ If `PklProject.deps.json` is missing, run: `cd Sources/ExFigCLI/Resources/Schema
1515
Schemas: `Sources/ExFigCLI/Resources/Schemas/{ExFig,Common,Figma,iOS,Android,Flutter,Web}.pkl`
1616
Output: `Sources/ExFigConfig/Generated/*.pkl.swift` (committed to repo)
1717

18+
## Codegen Side Effects
19+
20+
`codegen:pkl` rewrites ALL 9 `Generated/*.pkl.swift` even when one schema changed, and the
21+
generator omits the trailing newline. This (a) breaks the `hk` `newlines` (end-of-file-fixer)
22+
hook and (b) leaves 8 unrelated files with a 1-line newline-only diff.
23+
Fix: `git checkout --` the files whose schema you didn't touch, then
24+
`./bin/mise exec -- hk util end-of-file-fixer --fix Sources/ExFigConfig/Generated/<Changed>.pkl.swift`.
25+
1826
## Type Mapping
1927

2028
| PKL | Swift Generated |

‎.claude/rules/troubleshooting.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,5 @@ Common problems and solutions when working with ExFig.
5858
| DocC `+=` infinite redirect | `--hosting-base-path` adds prefix — omit for local preview, use only for GitHub Pages |
5959
| DocC preview port 8080 | `widget-simulator-service` occupies 8080 on macOS — use port 9090 or kill it |
6060
| DocC icon invisible dark mode | `currentColor` in SVG doesn't work in `<img>` — use `~dark.svg` with `#ffffff` stroke |
61+
| hk newlines hook fails on commit | `codegen:pkl` strips trailing newline from `Generated/*.pkl.swift`. Fix: `hk util end-of-file-fixer --fix <file>`; verify with `hk check --all` (exit 0). Hooks run via `bin/mise x -- hk run pre-commit` |
62+
| `Document`/`Paint` has no `visible` | swift-figma-api `Document`/`Paint` expose only `opacity` (not `visible`); only `Effect` has `visible`. Detect hidden layers via `opacity == 0` |

0 commit comments

Comments
 (0)