Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esbuild creates unintended non-dynamic chunks? #1716

Open
use opened this issue Oct 26, 2021 · 8 comments
Open

esbuild creates unintended non-dynamic chunks? #1716

use opened this issue Oct 26, 2021 · 8 comments

Comments

@use
Copy link

use commented Oct 26, 2021

Hello,

I'm 100% sure this is something I'm doing wrong but I was wondering if someone can point me in the right direction?

I'm coming from a webpack config, if that helps.

Here is my esbuild command:

npx esbuild src/js/theme.js --bundle --define:process.env.NODE_ENV=\"production\" --sourcemap --minify --analyze --splitting --format=esm --outdir=js

I'm using codesplitting so I can dynamically load chunks when they're needed. I'm using the dynamic import() function. This actually works great. Those chunks look like this in the --analyze output:

js/program-explorer-W7FFKVTU.js                                                  20.1kb  100.0%
   └ src/modules/program-explorer/program-explorer.tsx                             19.8kb   98.4%

However, I'm also getting "unintended" chunks. I am not loading them dynamically in any way that I'm aware of - they're being imported with plain old static import. The outputted chunks look like the following. Notably, the chunks have no real name in the prefix.

js/chunk-JPIFNYYQ.js                                                              2.4kb  100.0%
   ├ src/modules/course-popup/course-popup.jsx                                      1.4kb   57.7%
   └ src/modules/ewu-popup/ewu-popup.jsx                                            900b    36.8%

I guess my expectation is that when I use static import, the bundler would inline those modules rather than splitting and dynamically loading them. And I can't figure out why these statically loaded modules are being split into chunks.

@hyrious
Copy link

hyrious commented Oct 27, 2021

The extra chunk can exist because of common dependencies of your entrypoints including src/js/theme.js and the dynamic-imported ones.

Let me give you a minimal example:

// a.js
import { d } from "./c.js"
console.log('a', d)
import("./b.js")
// b.js
import { c } from "./c.js"
console.log('b', c)
// c.js
export const c = 1
export const d = 2

As you can see both a.js and b.js depend on c.js, turns out c.js is a common chunk. So:

// esbuild a.js --bundle --format=esm --splitting --outdir=dist --analyze
  dist/a.js               100b   100.0%
    a.js                  58b    58.0%

  dist/b-63JZQOHX.js       73b   100.0%
    b.js                  21b    28.8%

  dist/chunk-XF7GIIAF.js   52b   100.0%  // <- see, it doesn't has a name
    c.js                  22b    42.3%

The output code is almost the same as the input ones. Now you may know how esbuild's code splitting works.

@evanw
Copy link
Owner

evanw commented Oct 27, 2021

Code splitting splits on any shared code, either statically or dynamically imported. This is documented here: https://esbuild.github.io/api/#splitting. This behavior is intended. It both reduces downloaded code when moving between entry points on different pages and fixes correctness issues due to duplicate module instantiation if multiple entry points are included in the same page (either static or dynamic).

@use
Copy link
Author

use commented Oct 27, 2021

In my situation I only ever have 1 entry point. With these chunks being splitted, every page has 5 "extra" chunks which get downloaded. I'm working with the assumption that I should avoid extra requests within reason, but also avoid downloading + parsing unneeded code (hence why I am using splitting).

Maybe there's a different way I should structure things?

Or am I overvaluing the downsides of downloading e.g. 5 extra small chunks?

@Mwni
Copy link

Mwni commented Dec 31, 2021

While the implementation seems very efficient, it slightly misses the point: reducing load times. The few kb saved by splitting everything up into multiple chunks make up less of an impact than the sequential import of said chunks, which is inevitable using ESM imports; The browser can't parallelize the downloads. The usual application has one entry, with peripheral content like async components, pages or widgets. Coming from Rollup, this is how developers are used to the concept.

@garygreen
Copy link

garygreen commented Aug 4, 2022

While the implementation seems very efficient, it slightly misses the point: reducing load times. The few kb saved by splitting everything up into multiple chunks make up less of an impact than the sequential import of said chunks

+1. By default esbuild seems to chunk even the slightest amount of data.

Webpack has a default specific set of criteria on when it chunks - it prefers bigger chunks because it's actually counterintuitive to create lots of mini chunks when they are small because the additional HTTP overhead outweighs simply inlining that chunk.

I would love for esbuild to provide more control over when it decides to chunk. E.g. only chunk for files larger than 5KB, chunk together if they are in node_modules, etc.

Overall this issue relates to: #207 - being able to configure when chunking occurs, much like Webpacks splitChunks config.

@RomanHotsiy
Copy link

RomanHotsiy commented Aug 26, 2022

+1 for having some way to disable static chunks, e.g. split only on dynamic imports, even sacrificing the correctness of the import order and code size.

With a not-too-complex real-world application, we're getting tons of tiny chunks:

image

@alexblack
Copy link

Seems like an issue to us too... Our build produces 91 chunks, about 50% of those are 1kb or smaller

@abettadapur
Copy link

Figma is also trying to migrate to a splitting solution, and we are also producing a lot of chunks (I think in testing, we are at 20 critical chunks). For every dynamic import we add, we increase the number of emitted chunks by a large amount. I suspect it will get even worse as we add more dynamic imports, because the number of permutations increases.

I think adding at least a minChunkSize would go a long way here, even if the browser ends up having to download slightly more code. Is addressing this on the roadmap at all?

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

No branches or pull requests

8 participants