refactor(ui): wire the dimension classes to their tokens, and guard the token files - #184
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (0.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## chore/prune-unused-tokens #184 +/- ##
============================================================
Coverage ? 68.55%
============================================================
Files ? 213
Lines ? 8596
Branches ? 0
============================================================
Hits ? 5893
Misses ? 2703
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
8c4696c to
5a8684e
Compare
ad73473 to
be26581
Compare
3190680 to
2acd7cd
Compare
StreamSpacing, StreamRadius and StreamTokensTypography hard-coded the same numbers the vendored token files already held, so the design system's dimensions were mirrored twice with nothing tying the copies together — the swatch-versus-baked-hex hazard one layer up, where an upstream change can be applied to one and not the other in silence. The three classes now read the tokens. The type ramp gains from it: `height: 1.1111111111111112` becomes `typographyLineHeightNormal / typographyFontSizeLg`, which says what the number is instead of leaving a magic constant. Every ratio resolves bit-identically, so no golden moves. Adds internal/tokens/stream_tokens_dimensions.dart, beside the mode-independent typography file rather than under light/ and dark/, because the token repo publishes one set of dimensions for every mode — all 180 constants were byte-identical between the two files. Values come from the web flavor, the only one carrying the Geist family this package ships. They are typed `double` so they satisfy Radius, EdgeInsets and TextStyle directly. Two tokens are deliberately not carried, since nothing can read them: font weights, because TextStyle.fontWeight takes a FontWeight that cannot be built from a number in a const expression, and radiusNone, because the analyzer's use_named_constants prefers Radius.zero over circular(0). Also extends check_tokens.dart to hold the dimension file to the same rule, which is what caught radiusNone and a stale skip that hid the typography file's own reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The skill still said the dimension classes hard-code their values and that a change has to be applied to them by hand, which this branch makes false. It now says a dimension is a value edit in stream_tokens_dimensions.dart and the classes follow, records why font weights and radiusNone are absent, and names melos run check:tokens as what enforces the rule — so there is no judgment call about what belongs in a token file. Also brings three now-stale details in line: the file table gains the dimensions row, the reader list gains the three dimension classes, and After editing runs check:tokens next to analyze. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The skill gained this in the previous commit, but CLAUDE.md still described `StreamSpacing`, `StreamRadius` and `StreamTokensTypography` as hard-coding their values, which this PR is what changes. It now points at `stream_tokens_dimensions.dart` and at `check:tokens`, so the repo overview and the skill agree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pen paths `StreamTokensTypography` was wired to the dimension tokens, but nothing read it — not one Dart file in the repo, only prose. Meanwhile the public, exported typography surface still hard-coded every number. So the PR's premise held for `StreamSpacing` and `StreamRadius` and not for type: the 10 font-size and line-height constants were kept alive by a dead intermediary. `StreamLineHeight` now reads the three line-height tokens, which match exactly. The dead class is deleted, and with it the seven `typographyFontSize*` constants and the font family it was the only reader of. Font sizes stay unvendored on purpose: `StreamFontSize` ships an android and an ios scale, only android matches the web values, and the ios flavor is not vendored — wiring one and not the other would read as an oversight rather than a choice. That leaves the file mode- *and* flavor-independent, since spacing, radius and line heights are identical across android, ios and web. The guard would have caught this had it looked. It did not, and it had four other ways to pass without checking anything — each confirmed by running it: - Empty both color files and it reported `✓ 0 color tokens … all referenced`. - Delete the dimensions file and it reported `✓ … and 0 dimension tokens`. - A `--dir-exists` filter that stops matching selects zero packages, and melos prints SUCCESS. The one refactor most likely to need this guard — moving `internal/` — is the one that silently disabled it. - A dead token stayed "referenced" via a mention in a doc comment, and `comment_references` is off, so that is not even a lint. So: every "nothing to check" path is now a failure. Minimum declaration counts, a scanned-file count in the success line, comments and strings stripped before matching, a declaration-count cross-check so an unparsed form cannot hide, an error on any vendored file the check does not cover, and stale allowlist entries reported. The melos filter is gone — it runs once from the root and the script locates the package itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Typography was left half-wired: `StreamLineHeight` read its tokens but
`StreamFontSize` and `StreamFontWeight` still hard-coded theirs, on two
arguments that do not survive checking.
The first was that font sizes could not be wired because only the android scale
matches the values this package vendored. True, but the fix is to vendor both:
the token repo publishes an ios flavor whose eight values match
`StreamFontSize.ios` exactly. They now mirror upstream's split in
`internal/tokens/{android,ios}/stream_tokens_font_size.dart`, keeping the bare
upstream names, and feed the two scales.
The second was that a `FontWeight` cannot be built from a number in a const
expression. That is true of the raw 400/500/600/700 but does not mean the token
has no home — the vendored constants are already declared in the types Flutter
consumes rather than the types upstream emits, which is why the rest are
`double`. The weights are `FontWeight` on the same reasoning, and
`StreamFontWeight` reads them.
Both rested on a third claim, that only the web flavor carries the `Geist`
family this package ships. This package ships no text font at all — the one
bundled face is the generated `Stream Icons` — and never sets `fontFamily` for
text. So the flavor question was only ever about sizes, and the corrected note
says so.
All 23 wired values are identical to the literals they replace; `flutter test`
is `+404 -56`, the 56 unchanged.
`check:tokens` grew a matching branch, and the hardening earned itself out
immediately: the uncovered-file check caught both new flavor files before they
were wired in. It now also requires android/ and ios/ to declare the same names,
and that each flavor file is actually imported — they declare identical names,
so an orphaned one would otherwise still look referenced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2acd7cd to
98d3734
Compare
Third in the stack: #182 → #183 → this. Base is
chore/prune-unused-tokens, so the diff is only the wiring.Why this exists
StreamSpacing,StreamRadiusand the line-height scale hard-coded the same numbers the vendored token files already held. The design system's dimensions were mirrored twice, with nothing tying the copies together — the swatch-versus-baked-hex hazard one layer up, where an upstream change gets applied to one and not the other in silence.An earlier revision of #183 deleted those unread constants instead. That removed the evidence of the gap rather than the gap, which is why they came back here and got wired.
What changed
StreamSpacing,StreamRadius,StreamLineHeight,StreamFontSizeandStreamFontWeightnow read their tokens. All values are bit-identical to the literals they replace — verified by resolving every expression and comparing IEEE-754 representations, not just==. No golden moves.New
internal/tokens/stream_tokens_dimensions.dartsits beside the per-mode color files rather than underlight//dark/, because the token repo publishes one set of dimensions for every mode — all 180 constants were byte-identical between the two files. Typeddoubleso they satisfyRadius,EdgeInsetsandTextStyledirectly.Font sizes mirror upstream's flavor split. iOS runs a size up at almost every step, so the token repo publishes a separate set and
StreamFontSizeships two scales. Both are vendored, ininternal/tokens/{android,ios}/stream_tokens_font_size.dart, keeping the bare upstream names. Everything else — spacing, radius, line heights, weights — is identical across android, ios and web and lives in the one shared file.Weights are declared as
FontWeight, not the raw 400/500/600/700. The vendored constants are already expressed in the types Flutter consumes rather than the types upstream emits — that is why the rest aredouble— andFontWeighthas no public constructor from a number, so aninthere could not be read in a const expression.One upstream dimension is deliberately not carried:
radiusNone, since the analyzer'suse_named_constantsprefersRadius.zeroovercircular(0). The font family is not carried either — this package never sets one for text, and ships no text font, only the generatedStream Iconsface.Two corrections found in review
The first revision wired
StreamTokensTypography, an internal class of 20TextStyleconstants that nothing in the repo read — not one Dart file, only prose — while the public, exportedStreamLineHeightstill hard-coded its values. Ten constants were being kept alive by a dead intermediary. That class is gone, andStreamLineHeightreads the three line-height tokens.The second was this PR claiming font sizes and weights could not be wired at all. Both arguments rested on the idea that only the web flavor carries the
Geistfamily "this package ships". It ships no text font and never setsfontFamilyfor text, so the flavor question was only ever about sizes — and those are vendored per flavor now, with the weights alongside them.All 23 wired values are identical to the literals they replace.
The guard
scripts/check_tokens.dart, wired asmelos run check:tokensnext tocheck:barrelsin the same CI step. It fails when a vendored constant is never referenced, and whenlight/anddark/disagree about which constants exist — the second because a field resolving from a constant in one mode but not the other silently falls back.It earned its place while being written: it caught
radiusNonehaving no reader, and a stale directory-wide skip that was hiding a file's reads.It has since been hardened against passing vacuously. A guard that exits 0 because it found nothing to inspect is worse than no guard — it prints the same tick as a real pass. Review found five such paths, each reproduced by running the script:
✓ 0 color tokens … all referenced✓ … and 0 dimension tokensPlus a declaration-count cross-check, so a form the pattern cannot parse fails loudly instead of becoming exempt; stale allowlist entries are reported; and the success line now names how many Dart files were scanned, so "nothing unreferenced" can be told apart from "nothing examined".
The melos entry no longer uses
melos execat all. Any package filter that stops matching selects zero packages and reports SUCCESS, so it runs once from the repo root and the script locates the package itself.The allowlist is intentionally empty, and an entry matching no declaration is now a failure.
Verification
analyzeclean across all four packages;format:verify,check:barrelsandcheck:tokenspass.flutter testis+404with the pre-existing 56 macOS-golden failures unchanged, so nothing rendered differently.Both the guard's positive controls (dead constant, light/dark divergence) and all five hardened paths were confirmed by running the script against a deliberately broken tree and checking real exit codes.