Lazy-load Defuddle and the reader's Vue app in inject-css - #910
Draft
ankit wants to merge 1 commit into
Draft
Conversation
apply-state.ts imported applyReadability/removeReadability from the full @stylebot/readability barrel, which unconditionally pulled Defuddle and the reader's own Vue app into the always-eager inject-css bundle — paid on every page load regardless of whether that page ever uses Reader Mode. applyReadability is now behind a dynamic import(), code-splitting that ~460KB out into its own chunk fetched only on pages where a saved style actually has readability enabled. removeReadability (used on the far more common "readability off" path) doesn't need Defuddle/Vue at all, so it now bypasses the barrel directly instead, same as the existing isReaderable import. Requires overriding __webpack_public_path__ for this content script, since its document is the target page's own origin, not the extension's — otherwise webpack's default chunk-loading tries to fetch the dynamic chunk from the page being styled instead of the extension. The two resulting chunk files are covered by a new *.chunk.js entry in web_accessible_resources. Unlike the on-demand editor-injection change this stacks on, this one isn't Chrome/Edge-specific — Firefox shares the same inject-css code and benefits identically. Co-Authored-By: Claude Sonnet 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.
Stacked on #908 — this targets that branch, not
main.Summary
Following up on the perf discussion in #908:
inject-css's own eager bundle was still surprisingly large (~592KB raw / 166KB gzip) even after movingeditor/index.jsoff the eager path. Investigated with webpack's module stats and found why —apply-state.tsimportedapplyReadability/removeReadabilityfrom the full@stylebot/readabilitybarrel, which unconditionally pulls in:defuddle(the Readability parsing engine): 335 KBvueruntime (needed only for the reader's own tinyApp.vue): 227 KBThat's ~460KB of minified JS shipped and evaluated on every page load for every user, even though only a minority ever have Reader Mode enabled on any site.
Change
applyReadabilityis now behind a dynamicimport('@stylebot/readability'), code-splitting Defuddle + the reader's Vue app into their own chunk(s), fetched only on pages where a saved style actually hasreadability: true.removeReadability(the far more common "readability off" path) doesn't touch Defuddle/Vue at all, so it now imports directly from../readability/lifecycle/remove-readability, bypassing the barrel entirely — same techniqueinject-css/index.tsalready used forisReaderable.__webpack_public_path__tochrome.runtime.getURL('/')for this entry, guarded so it's a no-op underts-jest(no webpack runtime).readability-lazy.chunk.js+vendors~readability-lazy.chunk.js) are covered by a new*.chunk.jsentry inweb_accessible_resources.inject-csscode used identically by Firefox, so Firefox benefits too. Verified the Firefox build also produces and correctly references the chunks.The one deliberately-preserved behavior:
applyReadability()'s call itself stays fire-and-forget fromapply-state.ts(not part of its returned/awaited promise) — same as before — since that promise's resolution timing drivesrevealPage(), and coupling it to Reader Mode's own async mount (which waits onDOMContentLoaded/load) would have been a much bigger, riskier behavior change than intended here.Perf impact
Measured on this branch vs. its base (#908), same production webpack config:
inject-css/index.js(eager)A further ~75% reduction in the eager bundle for any page that doesn't have Reader Mode enabled — on top of #908's already-large win from making
editor/index.json-demand.Test plan
yarn typecheck,yarn lintyarn test— updatedapply-state.test.tsfor the new import paths, all 238 tests passyarn test:e2e— newlazy-readability-import.spec.tsasserts (a) the chunk is never fetched when readability is off, and (b) it loads successfully (200 responses) when on — directly exercising the dynamic import + publicPath override + web_accessible_resources wiring in a real browser, independent of Defuddle's own content-extraction heuristicsBROWSER=firefoxbuild also produces the chunks and inherits the manifest entry correctlyNote: the full e2e suite is reliable run serially (
--workers=1, confirmed twice); under this sandbox's default parallelism a couple of tests intermittently time out from what looks like multiple simultaneous Chrome instances contending for resources on this machine, not a functional issue — worth a sanity check in CI.🤖 Generated with Claude Code