Tree shaking doesn't work with Typescript barrel files #63494
Replies: 82 comments 2 replies
-
Feel free to investigate and solve |
Beta Was this translation helpful? Give feedback.
-
Same happening with jsx also. |
Beta Was this translation helpful? Give feedback.
-
Has anyone been able to solve this? In one of our projects we seem to be getting no tree shaking at all for any TS application code, only for third party library code. Alternatively, could any of the maintainers provide some hints as to how one would best go about investigating and solving such an issue? EDIT: For posterity, the problem in our case was that the bundle analyzer's output is harder to interpret correctly than it appears, which we failed to do initially. It seems that tree shaking was not the problem. |
Beta Was this translation helpful? Give feedback.
-
@seeekr We actually ended up just removing our barrel files. I did a simple experiment with Webpack and a couple of JavaScript modules + a barrel file (no Next, or Typescript) and the same behaviour was present with limited options to enable tree shaking to occur. If you have no side effects in your code then you may be able to look into the Webpack side effects setting to allow this to work for you, but as soon as you're importing a module classed as a side effect, you may start to notice issues. For us, our polyfill imports stopped working along with a whole host of other stuff. I'm not sure this is something Next can/should solve though, as it seems to be inherent to Webpack |
Beta Was this translation helpful? Give feedback.
-
@stevethatcodes @majelbstoat Tree shaking seems to work for me as long as I specify side effects in package.json: "sideEffects": [
"./src/some-side-effectful-file.js"
] This is according to the Webpack docs linked above. I think you would just have to determine where your polyfills and other side effects are happening and add those files to the side effects list. |
Beta Was this translation helpful? Give feedback.
-
@VWSCoronaDashboard8 Yeah, that's an approach we tried, though still had issues with the polyfills not loading. In fairness, we took the approach to remove barrel files as the path of least resistance, as they didn't add much value and we didn't need to do anything else to our Next/Webpack setup to bring back tree-shaking. |
Beta Was this translation helpful? Give feedback.
-
@sphilee I don't actually think it works / resolves the issue, I've installed Webpack 5 on my current Next.js projects but it doesn't have any positive impact on the bundle size, at least for barrel files structured as the issue example by @majelbstoat. |
Beta Was this translation helpful? Give feedback.
-
@RobbyUitbeijerse @sphilee I have concluded the same. Webpack 5 didn't solve this issue for me. |
Beta Was this translation helpful? Give feedback.
-
Based on the following issue webpack/webpack#11821 , Webpack 5 should actually eliminate dead code with the minimize option being enabled (which is the case by default for What I'm concerned about is that it might actually work for the shared bundle which is loaded for all pages, but doesn't actually consider the page by page chunks, meaning that while dead code is actually eliminated from the shared bundle - you are still left with a single bundle containing everything instead for smaller chunks per page that only contain what you need for that page. Quite unsure whenever it's possible to actually resolve that or that changing the imports is the only way to go. |
Beta Was this translation helpful? Give feedback.
-
@timneutkens Webpack is not my strong suit, but do you have any clue if my comment above makes any sense in terms of what we are seeing? |
Beta Was this translation helpful? Give feedback.
-
I had similar issues in the past when using In the past we also had issues with babel when it came to exporting types. Since then we started exporting types explicitly using
|
Beta Was this translation helpful? Give feedback.
-
I actually started doing the same recently: But it turns out, no luck: What I did find tho, that when I put all of the components in an external library and build that library using Rollup while preserving the modules, tree shaking of the same components seems to work properly, meaning that it should be possible to get it all working one way or the other. [edit] Same result with webpack 5 |
Beta Was this translation helpful? Give feedback.
-
Same experience here. I was pulling my hair out trying to figure this out. I have a monorepo, and in the I can make a repro in a monorepo this weekend. I'm using Next |
Beta Was this translation helpful? Give feedback.
-
This has been open for over a year. Has anyone found a solution yet? Using a barrel file results in my bundle reaching 12mb whereas without I'm less than 1mb |
Beta Was this translation helpful? Give feedback.
-
@JoeyFenny |
Beta Was this translation helpful? Give feedback.
-
The very idea that we have to manually list libraries that were previously tree-shaken automatically is... not inspiring. |
Beta Was this translation helpful? Give feedback.
-
@ivan-kleshnin Can you share a |
Beta Was this translation helpful? Give feedback.
-
@ivan-kleshnin out of curiosity, why did you thumbs down ESLint plugin I suggested? It is working great for us |
Beta Was this translation helpful? Give feedback.
-
Regardless of whether it was or wasn't done automatically previously, can you explain why it makes sense to manually specify a list of package to optimize? Isn't it always a good default to optimize dependencies? From the docs, it is not clear in what scenario it is helpful to "not optimize packages". From what I can tell, the default should be to optimize/treeshake all dependencies, at least for production, with perhaps an opt-out, although I don't see a reason for that either. Again, maybe I'm missing some context but this seems to me like really awful API design when considering the principle of "choose good defaults" and "allow users to opt into necessary exceptions". |
Beta Was this translation helpful? Give feedback.
-
I'm not the downvoter but couple of reasons:
|
Beta Was this translation helpful? Give feedback.
-
Because that plugin simply "Restricts the use of barrel files". I don't see barrel files as something bad that should be avoided, me/my teams in the present/past heavily used them outside of NextJS context, with great success. They are convenient, and I'm not aware of any cons, except that they cripple NextJS... So why I should lint myself against NextJS issue? 🤔 If they aren't going to fix... ok, we have a growing list of complaints against this framework and React anyway, might be a good final reason to switch out. |
Beta Was this translation helpful? Give feedback.
-
I came across this issue when using react-aria-components. When using just a simple While reading this thread, I saw this issue linked above: #44039 It seems when using I detailed the issue here: #60206 |
Beta Was this translation helpful? Give feedback.
-
@ivan-kleshnin As far as I'm aware, there is a cost to using barrel files, which is why we recently introduced I agree that it is not ideal to manually have to add packages to next.js/packages/next/src/server/config.ts Lines 710 to 765 in 12e8881 Ideally, we just automatically detect packages that need to use |
Beta Was this translation helpful? Give feedback.
-
@samcx optimizePackageImports doesn't work in my case when using react-aria-components. I detailed the issue in the comment above. Using Vite works correctly. Also, using "use client" kinds gives okish results, but there is a bigger problem when you have multiple pages. Again? Detailes in the discussion I listed above |
Beta Was this translation helpful? Give feedback.
-
@IonelLupu Thank you for re-iterating your issue involving react-aria-components and creating a detailed discussion post. I have created #60246 from your discussion post and will be taking a look! |
Beta Was this translation helpful? Give feedback.
-
Update: there was a fix for the above issue that entailed importing direct Client Components in Server Components. |
Beta Was this translation helpful? Give feedback.
-
Maybe this isn't related, but I came across this issue when using plaiceholder, which is supported only in server components. I have barrel/index file which re-exports both server and client "elementary" components. During development when some page imports client component from the barrel file, the app crashes on error "ReferenceError: require is not defined". So it looks like the server component is automatically imported/loaded and gets into the browser despite the fact that it is marked as server component (without "use client" at the top) and is not used anywhere on the page. Simplified components are: // ServerImageWithBlur.tsx
import { getPlaiceholder } from "plaiceholder"
export const ServerImageWithBlur = async (props: ImageProps) => {
const { base64, img } = await getPlaiceholder(props.src)
return <Image placeholder="blur" blurDataURL={base64} {...props} {...img} />
} // Navbar.tsx
"use client"
export const Navbar = () => {
return <div>...</div>
} // index.ts
export { ServerImageWithBlur } from "./ServerImageWithBlur"
export { Navbar } from "./Navbar" Error:
When I comment the re-export of the server component from // index.ts
// export { ServerImageWithBlur } from "./ServerImageWithBlur"
export { Navbar } from "./Navbar" |
Beta Was this translation helpful? Give feedback.
-
@tocosastalo Can you create a new separate bug report so we can take a look? In the meantime, I will be moving this over to Next.js discussions, since we have veered from the original issue posted back in 2020. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
Bug 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:
Each component file exports a single component, like this:
index.ts is a barrel file that re-exports from each individual component file:
I then use a couple of components in _app.tsx like:
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:
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:
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
Beta Was this translation helpful? Give feedback.
All reactions