Skip to content

build(bundle): the app ships as one bundle - #305

Merged
JanWelker merged 1 commit into
mainfrom
build/single-bundle
Sep 8, 2026
Merged

build(bundle): the app ships as one bundle#305
JanWelker merged 1 commit into
mainfrom
build/single-bundle

Conversation

@JanWelker

Copy link
Copy Markdown
Owner

Firefox's network tab on kneadtime.pizza showed 18 requests for one visit: eleven JS chunks, two stylesheets, two fonts, the document and the favicon. Six of those scripts were under a kilobyte — request overhead and nothing else — and all eleven were fetched on every visit, so the split was buying nothing.

It also cost a wave. app.js had to arrive and parse before the last node chunk was even discovered, and the webfonts queued behind that: they did not start until 132 ms on a local preview.

What changed

kit.output.bundleStrategy: 'single' in svelte.config.js. Everything collapses to bundle.<hash>.js + bundle.<hash>.css.

Measured on the real build, loading /:

before after
requests 17 5
JS files 11 1
CSS files 2 1
JS gzip 131.0 kB 133.2 kB
fonts requested at 132 ms 122 ms

The +2.1 kB is Rollup losing a little cross-chunk dedup; on a real connection one round trip is worth more than that.

The trade-off

Cache granularity: any change re-downloads the whole bundle rather than the chunk that moved. That is close to theoretical on GitHub Pages, which serves every asset with cache-control: max-age=600 regardless of the immutable path — checked against the live site.

Why there is a test for a config line

Nothing else in the tree points at it, and the app behaves identically without it — only the waterfall gets longer, which is exactly the kind of regression a framework upgrade or a tidy-up removes unnoticed. e2e/self-hosted.spec.ts now counts the script and stylesheet responses on a real visit; reverting the config line fails it with twelve scripts. The spec was already the place that watches what the page fetches and from where, so this is the other half of the same question: how many times.

Not in this change

messages.ts is ~173 kB raw / ~56 kB gzip and every reader downloads five languages to use one — roughly 40 % of the JS. Splitting it per locale is the real byte win, but it is a genuine refactor (the i18n singleton is synchronous at every call site) and it conflicts with a single bundle, which inlines dynamic imports. Worth its own change and its own decision.

Checks

  • 953 unit tests, 100 % statements/branches/functions/lines
  • 127 browser tests (126 + the new one); test-baseline.json raised to match
  • npm run lint clean; version 7.0.4 (patch — no user-visible behaviour, no URL schema change)

🤖 Generated with Claude Code

The splitter produced eleven JS chunks and two stylesheets, and every one of
them was fetched on every visit — six under a kilobyte, which is request
overhead and nothing else. It also cost a wave: app.js had to arrive and parse
before the last node chunk was discovered, and the webfonts queued behind that,
so they did not start until 132 ms on a local preview.

kit.output.bundleStrategy: 'single' takes a visit from 17 requests to 5 — the
page, the bundle, the stylesheet and the two font subsets actually needed — for
+2 kB gzip, which is Rollup losing a little cross-chunk dedup. The fonts start
at 122 ms instead.

The trade is cache granularity: any change re-downloads the whole bundle rather
than the chunk that moved. That is close to theoretical on GitHub Pages, which
serves every asset with max-age=600 no matter what the immutable path claims.

It is one config line with nothing else pointing at it, and the app behaves
identically without it — only the waterfall gets longer, which nobody notices.
So e2e/self-hosted.spec.ts counts the script and stylesheet responses now;
reverting the line fails it with twelve scripts.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-08 18:02 UTC

@JanWelker
JanWelker merged commit e92f6fa into main Sep 8, 2026
6 of 7 checks passed
@JanWelker
JanWelker deleted the build/single-bundle branch September 8, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant