feat(config): add CSS custom-properties scraper - #114
Conversation
Adds `scrape_css_properties` that walks a list of CSS files, finds
`:root { ... }` blocks (top-level or wrapped in a single `@media` /
`@supports` at-rule), and surfaces every `--name: value;` declaration as
a typed `CssPropertyScrape`. Values are classified into hex/rgb/hsl
colors (normalized to `#rrggbb`/`#rrggbbaa`), `Px(u32)`, `Rem(f32)`,
`Em(f32)`, or `Other(String)` so callers like `plumb init` can fold them
into the matching config sections.
Hand-rolled scanner (skip comments, skip strings, brace depth) — chosen
over `cssparser` to keep the dep tree narrow given the limited scope.
Malformed input surfaces as `ConfigError::CssParse` with a miette span.
Closes #30
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I now have everything I need. The diff is entirely in PR #114 —
|
…ents The previous loop pushed `bytes[i] as char` for every non-comment byte, which truncates non-ASCII codepoints. Replace with a string-slice copy of each non-comment run so multi-byte values (e.g. unicode font names in `--font: "…"`) round-trip correctly. Addresses Claude code-review BLOCKER on #114.
Address claude-code-review feedback on PR #114: - Rename misleading `lower` binding in `strip_func` to `bytes`; the binding was never lowercased and bytes were re-computed on the next line. - Reconcile `Parser::slice` comment with code. The empty-string fallback is the actual defensive no-op for an unreachable branch (cursor positions only land on ASCII delimiters by construction); drop the misleading `from_utf8_lossy` suggestion. - Document output ordering on `scrape_css_properties`: results are emitted in input-file order, then by source position. Callers using `glob`/`read_dir` MUST sort first to keep output deterministic across runs.
Brings in #112 (color/palette-conformance rule) and #114 (CSS custom-properties scraper). Resolves a trivial conflict in plumb-config/src/lib.rs (both branches added a new mod + pub use; combine them) and corrects the CHANGELOG entry to reflect the lowered MAX_NESTING (256 -> 64) from the prior review-feedback commit.
Spec
Fixes #30
Summary
scrape_css_properties(inplumb-config) that walks a list of CSS files, finds:root { ... }blocks at the top level or wrapped in a single@media/@supportsat-rule, and surfaces every--name: value;declaration as a typedCssPropertyScrape.Color(hex/rgb/rgba/hsl/hslanormalized to#rrggbb/#rrggbbaa),Px(u32),Rem(f32),Em(f32), orOther(String)so callers (e.g.plumb init) can fold them into[spacing]/[color]/[type]/[radius].ConfigError::CssParsewith a miette span pointing at the offending region.Hand-rolled scanner (brace + semicolon state machine, skips comments + quoted strings) — chosen over
cssparserto keep the dep tree narrow given the narrow scope (:rootdiscovery only).Test plan
just checkpasses (fmt + clippy, no warnings)just testpasses on my machinejust determinism-checkpassescargo deny checkpassescrates/plumb-config/tests/css_props_scraper.rs(13 cases: top-level:root,@media-wrapped:root,@supports-wrapped:root, multiple:rootblocks, comments + quoted strings (semicolon inside string),remsurfaces asRem,emsurfaces asEmwarning,Otherfor font stacks / unitless,rgb()→ hex normalization, malformed CSS →CssParsewith span, missing file →Read, ignores non-:rootselectors, ignores non-custom declarations).plumb init).docs/src/**prose — N/A (nodocs/src/**changes).Screenshots / terminal output
N/A — no user-visible output yet. The function returns
Vec<CssPropertyScrape>for downstream consumers.Breaking change?
Anything reviewers should double-check
:rootselector match is strict (:rootonly, not:root, html). Issue spec is explicit about scope; expanding to selector lists is a follow-up.@media (...)/@supports (...)); deeper nesting is tolerantly skipped rather than erroring, matching the spec's "one level deep is enough".hsl(0, 100%, 50%) == #ff0000.🤖 Generated with Claude Code