Repository navigation
Description
While working on a few fixes I looked at the client build. Every storefront page loads the shared-home chunk, and two of the chunks vite.config.ts asks for are never created. I'm sharing the measurements rather than a PR, because the chunking setup is yours to decide.
Measured on master at af71eb8 (Vite 8.2.2), with pnpm build.
What I found
1. shared-home is the largest client chunk, and every page loads it.
| Chunk | Minified | gzip -9 | brotli |
|---|---|---|---|
shared-home |
530 kB | 161 kB | 137 kB |
The product, category, tag and static page routes, the cart route, index.client and hydrate all import it (grep -l shared-home dist/client/assets/*.js). The modulePreload.resolveDependencies filter keeps anything matching shared-, so it's also preloaded on every page. This is the chunk behind the build's "Some chunks are larger than 500 kB" warning.
2. shared-product and shared-common are never emitted. manualChunks returns them for ProductCard / ProductDetails / ProductGallery / AddToCartForm and for the engine's Breadcrumb / Image / Link / … modules, but dist/client/assets has only shared-home-*.js. The product page components end up inside shared-home instead.
3. What's inside shared-home. These figures are from rollup-plugin-visualizer's raw data for the client environment, as rendered sizes before minification, grouped by package:
| Rendered | Package / module |
|---|---|
| 253 kB | @salla.sa/twilight-theme-engine |
| 162 kB | swiper |
| 83 kB | @salla.sa/twilight-components-react |
| 74 kB | i18next |
| 64 kB | ky |
| 52 kB | sonner |
| 45 kB | @tanstack/query-core |
| 42 kB | @tanstack/virtual-core |
| 19 kB | app/components/icons.tsx |
| 15 kB | app/components/product/AddToCartForm.tsx |
| 12 kB | app/components/product/ProductDetails.tsx |
| 10 kB | app/components/common/ProductOptionsPanel.tsx |
| 9 kB | app/components/product/ProductGallery.tsx |
So the "home" chunk mixes three things:
- libraries every page needs (
i18next,ky,sonner, TanStack Query) - the product page components
- home-only code such as Swiper and the home blocks
Because every page needs the first group, every page also downloads the other two.
How I got the per-environment numbers: the visualizer writes dist/stats.html once per environment, so the server build overwrites the client's. Locally I set emitFile: true, filename: 'stats.json', template: 'raw-data' on it, which writes dist/client/stats.json and dist/server/stats.json. I didn't commit that.
Possible directions
Only if you want them; I haven't tried these against your CWV numbers.
- Check why the
shared-product/shared-commonrules have no effect under Rolldown. The build's hint points atbuild.rolldownOptions.output.codeSplittingas the native replacement formanualChunks. - Give the libraries every route needs (
i18next,ky,sonner,@tanstack/query-core) their own vendor chunk. Thenshared-homewould hold only home code, and could stay off theresolveDependenciespreload list on other pages. - Track the result with the size budget from ci(devops): add a bundle size budget with pnpm size #63.
Related: no-deep-component-import
The build warns that CartItem.tsx and AddToCartForm.tsx import ../common/ProductOptionsPanel instead of the ../common barrel. I tried following the advice, and it backfires here:
components/common/index.tsalso re-exportsBlockHookSlot, which imports@salla.sa/twilight-theme-engine/home.- So importing from the barrel makes the cart and product components load the engine's home module in dev and in tests. Four test files (
CartItem,CartPage,AddToCartForm,sold-out-sticky-bar) failed on the setup's@salla.sa/twilight-components-reactmock. - The production bundle was unchanged (1 byte), so it saves nothing.
I'd keep the deep import. The rule has no per-line opt-out, only disabledRules, so it may be worth a note for whoever owns the best-practices plugin.
I'm happy to help with a PR once you've picked a direction.
Activity