Conversation
tommy230
force-pushed
the
fix/tree-shake-selector-semantics
branch
2 times, most recently
from
September 29, 2026 00:02
9ff93c2 to
d1a9947
Compare
…ss rules The shaker read every registry class in a selector as a requirement, including classes inside :not() and each alternative of :is()/:where(). A negated class is the opposite of a requirement and :is() alternatives are each sufficient, so rules like `.nav > li > a:not(.cart-button)` were dropped whenever no node carried the negated class. selectorPartCanMatch now splits a part into the text whose classes are all required and the alternation groups where one alternative is enough; :has() and :nth-child(n of ...) stay required. For selectors the shared lexer reads correctly, drops are a subset of before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tommy230
force-pushed
the
fix/tree-shake-selector-semantics
branch
from
September 29, 2026 04:45
d1a9947 to
7dc8292
Compare
tommy230
marked this pull request as ready for review
September 29, 2026 04:47
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A rule that mentions a class only inside
:not(), or as one alternative of:is()/:where(), is dropped at publish when that class is not in use on the site. The stylesheet reads back correctly and the canvas renders it; only the live site loses the styling.A theme pads every nav link with
No node on the site carries
.cart-button, so publish drops the rule and every nav link sits flush against the next. Likewise:is(.a, .b) pis read as.a.b pand is dropped when only.bis in use.The cause is in the tree shake of the class registry, which drops an ambient rule when its selector names a registry class that no node uses.
selectorPartCanMatchinsrc/core/publisher/styleRuleTreeShake.tsscans the whole selector part withextractCssSelectorClassesand fails on any known-but-unused class, so it reads a negated class as a requirement and:is()alternatives as all required at once.CssSelectorClassToken.functionalDepthis not enough to fix it: depth cannot tell:not(.x)(never required) from:has(.x)or:nth-child(n of .x)(required), so the pseudo name has to be read.The fix:
:is()/:where()groups where one alternative is enough.:not(...)is removed outright, at any nesting. Quoted strings, attribute selectors and escapes are respected.:has()and:nth-child(n of ...)arguments stay required. Malformed pseudos (unbalanced parens, empty:is()) fall to "keep".For any selector the shared class lexer reads correctly, this only keeps more CSS than before, never less. A part is dead only if a known-unused class remains in the required text or every alternative of some
:is()group is dead, and both of those were already grounds for dropping under the old whole-part scan.The one exception is an escaped quote in an unquoted attribute value inside a removed pseudo, such as
.nav a:not([title=Don\'t]) .gone. The old scan read the\'as an opening quote, went blind for the rest of the selector and kept the rule by accident; the new scan reads past it and drops the rule, which is correct.Verification
bun run buildbun run lintbun test: 7060 pass, 0 failNew tests in
src/__tests__/publisher/styleRuleTreeShake.test.ts: 3 of 7 fail onmainand pass with this change.Checklist
docs/features/publisher.md.)🤖 Generated with Claude Code