feat(ledger): the consumer map reads transition names and concatenated prefixes (#2970)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Failing after 10s
CI & Build / integration (push) Successful in 26s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / Build & push image (push) Successful in 23s
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Failing after 10s
CI & Build / integration (push) Successful in 26s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 1m6s
CI & Build / Build & push image (push) Successful in 23s
Paying down #2962 measured `flag="unused-css"` against a hand audit and found it had a permanent false-positive floor: roughly sixty classes it called unused are alive and always would be, because two ordinary authoring forms produce names no reader of `class=` attributes can see. A flag whose list you cannot act on line by line is worse than no flag — act on it and you delete live UI. - A transition `name=` IS a class reference. `<Transition name="toast">` makes Vue apply `.toast-enter-active` and its siblings at runtime, and React's `<CSSTransition classNames="fade">` does the same with a different suffix set. Every spelling of the tag is read (`Transition`, `TransitionGroup`, `transition-group`), and the emitted suffix set is the union of Vue 3, Vue 2 and React: naming a class no rule defines costs nothing, since it resolves to no row. A bound `:name` stays unknowable. - A concatenated name contributes its static head as a PREFIX reference. `` `status-${s}` ``, `'pri-' + p` and `class="card-{{ v }}"` all leave a head behind once the hole is blanked — and `_CLASS_TOKEN_RE` accepts a trailing hyphen, so until now the extractor emitted a junk token `"status-"` that matched nothing. It is now `status-*`, and resolve_consumers credits every row whose symbol starts with that head, each under the same own-file-else-fan-out rule as an exact token. `*` cannot occur in a class token, so the marker rides the existing dict[str, int] with no schema change. A head shorter than two characters says nothing and is dropped. Crediting every candidate row is the honest reading: the template genuinely does not say which one it built, and the alternative is reporting live rules as dead. What the map still cannot see is a name assembled in a script — `classList.add` — which stays deliberately out of scope; the skill, the `flag=` docstring and the refresh payload docs all say so now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -279,20 +279,39 @@ def resolve_consumers(
|
||||
of that name when F defines it (a scoped rule is consumed by its own
|
||||
template); otherwise to every other file's row of that name — a shared
|
||||
sheet, or, when several files define it, all of them: the map says
|
||||
"ambiguous" by fanning out rather than guessing one."""
|
||||
"ambiguous" by fanning out rather than guessing one.
|
||||
|
||||
A token ending in ``PREFIX_MARK`` is a PREFIX reference (#2970) — the
|
||||
static head of a name the template concatenates, `status-*` from
|
||||
`` `status-${s}` ``. It stands for every row whose symbol starts with
|
||||
that head, each resolved by the same own-file-else-fan-out rule. The
|
||||
template cannot tell us WHICH of them it built, so the map credits all
|
||||
of them rather than calling live rules unused."""
|
||||
# Lazy, like the extract_definitions import below: coverage reaches into
|
||||
# this module during a refresh, so neither may import the other at load.
|
||||
from scribe.services.coverage import PREFIX_MARK
|
||||
|
||||
by_symbol: dict[str, list[tuple[int, str]]] = {}
|
||||
for sid, path, symbol in css_rows:
|
||||
by_symbol.setdefault(symbol, []).append((sid, path))
|
||||
out: dict[tuple[int, str], int] = {}
|
||||
|
||||
def credit(rows: list[tuple[int, str]], consumer: str, count: int) -> None:
|
||||
own = [sid for sid, path in rows if path == consumer]
|
||||
for sid in own or [sid for sid, _path in rows]:
|
||||
out[(sid, consumer)] = out.get((sid, consumer), 0) + int(count)
|
||||
|
||||
for consumer, tokens in references.items():
|
||||
for token, count in tokens.items():
|
||||
rows = by_symbol.get(token)
|
||||
if not rows:
|
||||
if token.endswith(PREFIX_MARK):
|
||||
head = token[: -len(PREFIX_MARK)]
|
||||
for symbol, rows in by_symbol.items():
|
||||
if symbol.startswith(head):
|
||||
credit(rows, consumer, count)
|
||||
continue
|
||||
own = [sid for sid, path in rows if path == consumer]
|
||||
targets = own or [sid for sid, _path in rows]
|
||||
for sid in targets:
|
||||
out[(sid, consumer)] = out.get((sid, consumer), 0) + int(count)
|
||||
rows = by_symbol.get(token)
|
||||
if rows:
|
||||
credit(rows, consumer, count)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user