Tree shaking doesn't work with Typescript barrel files
See original GitHub issueBug report
I originally raised this as a discussion, but now I think it’s a bug.
Describe the bug
When using a barrel file to re-export components from a single location, tree-shaking does not function correctly.
To Reproduce
I’m using Next 9.3.6 and I’ve arranged my components like:
components/
Header/
Header.tsx
Sidebar/
Sidebar.tsx
index.ts
Each component file exports a single component, like this:
export { Header }
index.ts is a barrel file that re-exports from each individual component file:
export * from './Header/Header.tsx'
export * from './Sidebar/Sidebar.tsx'
// ...
I then use a couple of components in _app.tsx like:
import { Header, Sidebar } from "../components"
There’s about 100 components defined, and only a couple are used in _app.tsx. But when I analyze the bundle I have a very large chunk, shared by all pages, and it contains all my components, resulting in an inflated app page size:
I use a similar import strategy within pages, and every one of them appears to contain every component.
my tsconfig.json is:
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "es5"
}
and I’m using next’s native support for the baseUrl field. I haven’t changed the module or the target.
When I change the _app.tsx imports to:
import { Header } from "../components/Header/Header"
import { Sidebar } from "../components/Sidebar/Sidebar"
the common bundle and app page size drops dramatically, as I would expect it to:
Expected behavior
The app page size should be the same using both import strategies.
System information
- OS: [e.g. macOS]
- Version of Typescript: [e.g. 3.8.3]
- Version of Next.js: [e.g. 9.3.6]
Issue Analytics
- State:
- Created 3 years ago
- Reactions:101
- Comments:48 (4 by maintainers)
Top GitHub Comments
Disabling side effects for my imported barrel libs works for me.
I don’t use side effects, seems like a lib thing to rely on that. Unless i’m sorely mistaken about side effects.
Barrel files are effectively an API. A specific interface that distinguishes private vs public code. It especially makes sense in a monorepo with local packages. The same concept (why do packages have a specific import interface) pros/cons applies to monorepo packages. Being in the package mindset is helpful in its own right re; reusability/scalability too.
My opinion: barrels are annoying (having to create them, maintain them, deal with circular imports), but denoting public vs private code is helpful in maintenance just like
private
andpublic
class methods.Less refactoring required when files change; only the “API” (barrel file) needs to be updated.
Hides implementation from consumers.
Clear what changes are necessary if anything in public API changes, or if nothing in public API changes.
Arbitrary files imported by arbitrary other files is harder to maintain and understand than a single barrel. The possible scope of any modification is larger without barrels.
Ultimately, we all use barrels, via libraries. The same reasoning “why do libraries not just expose all file paths” applies to our own code as well.
There are cons, but that’s not your question. Those are just my 2cents that come to mind right now.