fix(publisher): keep classes a runtime script toggles - #485
Open
borskyj-symph wants to merge 1 commit into
Open
Conversation
Publish tree-shakes the class registry against node class ids, so a rule survives only when some authored node carries its class. A modifier that only exists at runtime, toggled by a script, is carried by no node, so publish dropped it. The rule is present and correct at every point before publish. site_read_styles returns it, the canvas renders it, the stored document round-trips it. It is absent only from the published stylesheet, so a nav that opens in the editor does nothing on the live site and the search starts on the script and reaches the stylesheet last. Collect the identifier-shaped runs in each script file and treat a class whose name appears among them as used. Splitting the source on characters a CSS class name cannot contain covers classList.add, className assignment, template literals and lookup tables without modelling any of them, at the cost of over-collecting ordinary identifiers. Over-collecting is the safe direction: a false positive costs a few bytes of CSS, a false negative costs a feature that works everywhere except in production. Script-referenced ids are unioned into the used set and never subtracted, so this can only keep more CSS than before, never less. Widen collectUsedStyleRuleIds and usedStyleRuleIdSignature to take files and styleRules. Both callers already pass a whole site document, and routing the canvas through the same collection keeps the editor and the publisher agreeing on which rules are live. The run set is cached on files-array identity because the signature helper runs inside a canvas store selector. Fixes CoreBunch#465 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
What
Fixes #465.
Publish tree-shakes the class registry against node class ids:
collectUsedStyleRuleIdswalks page and Visual Component nodes, andtreeShakeStyleRuleskeeps a class rule only when some authored node carries it. A modifier that only exists at runtime, toggled by a script, is carried by no node, so publish treated it as dead and dropped it.The rule is present and correct at every point before publish.
site_read_stylesreturns it, the canvas renders it, the stored document round-trips it. It is absent only from the published stylesheet, so the menu opens in the editor and does nothing on the live site. That shape is expensive to debug: you trust what reads back, so you start on the script, then event binding, and reach the stylesheet last.The workaround is to apply runtime state as inline styles from script, which gives up the cascade entirely — no media queries, no pseudo-states, no theming on that state.
How
Collect the identifier-shaped runs in each
scriptsite file and treat a class whose name appears among them as used.Splitting the source on characters a CSS class name cannot contain (
/[^A-Za-z0-9_-]+/) coversclassList.add,classNameassignment, template literals and lookup tables of state names without modelling any of them. It over-collects:add,lengthand every other identifier in the file land in the set, so a class named after one of them is kept even when no script really references it.Over-collecting is the safe direction here. A false positive costs a few bytes of CSS. A false negative costs a rule that is correct everywhere until publish drops it and the feature dies in production with nothing to point at.
This can only keep more CSS than before, never less. Script-referenced ids are unioned into the used set and never subtracted, so no rule that survives today can start being pruned by this change.
Of the three options the issue proposes, this is the one that needs no author opt-in and adds no config surface. Happy to switch to a safelist or a per-rule keep flag if you would rather have an explicit mechanism.
Scope notes
runtime.scripts[id].enabledwould be more precise but couples this module to runtime config normalization, and a disabled script's classes surviving is harmless under the over-collect argument above.usedStyleRuleIdSignaturewidened too, so the canvas and the publisher agree on which rules are live rather than diverging on this one point. Both callers already pass a whole site document, so the widerPickneeds no caller changes.type: 'script'files count. A class name mentioned in adocorconfigfile does not keep a rule alive; there is a test pinning that.Tests
Three tests in
src/__tests__/publisher/classStyleInjector.test.ts:mainmainChecks
bun run lintandbun run build(tsc -b) clean.bun test src/__tests__/publisher/ src/__tests__/canvas/— 782 pass, 0 fail.The full
bun testrun has ~226 failures on this Windows checkout onmainas well, all in collab/socket/SQLite-temp-file areas (EBUSY: resource busy or lockedon temp DB cleanup). That set is unchanged by this PR.