-
Notifications
You must be signed in to change notification settings - Fork 27.4k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
Tree shaking doesn't work with Typescript barrel files #12557
Comments
Feel free to investigate and solve |
Same happening with jsx also. |
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. |
@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 |
@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. |
@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. |
@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. |
@RobbyUitbeijerse @sphilee I have concluded the same. Webpack 5 didn't solve this issue for me. |
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. |
@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? |
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
|
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 |
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 |
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 |
@JoeyFenny |
I am running into simular problems in a non next.js project. Upgrading from webpack 4 to webpack 5 did not fix it, sideEffects: "false" in the package.json does enable tree shaking but the barrel pattern still fails. I think this is a webpack problem, maybe we should move the dicussion there? Important edit: It seems I was wrong that tree shaking wasn't fully working in my webpack project because of the barrel pattern. The barrel pattern (vs direct importing) made a lot more code be processed which increased the chance of tree shaking not being applied because of sideEffects within that code. I don't know OP's exact case or next.js's handling when it comes to tree shaking vs webpack 5 but I did came accross a blog post that goes more in-depth on tree shaking that I would recommend reading: https://dev.to/livechat/tree-shaking-for-javascript-library-authors-4lb0 In the end I had to add Again, I don't know if OP has the same issue, or there is some config issue or if it is next.js that is failing, but what I did found out is that webpack 5 does support the barrel pattern. |
settings sideEffects to false and adding |
We dont have any news or ideas about this? I am really struggling to fix this since its having a big impact in our page load. |
For tree-shaking to work with a barrel file you need
|
sideEffects: false, makes tree shaking work with barrels files vercel/next.js#12557 Mapping only needed data from posts Change first render tweet info date
@sokra I want to have barrel files as the |
I have to agree. All these micro-optimisations and then you have this which would probably save 100s of kb being sent on every route on most nextjs apps. Don't get it. |
@leerob - any ETA on a solution for this? |
I'm so happy I decided to make the changes early even if it took me some time to do it. It would have taken me more time today. Crazy to see this is still not resolved. Next step might be to move the project to Astro 🤔 |
Randomly stumbled upon this, but I highly recommend ESLint rule that restricts use of barrel files https://github.com/gajus/eslint-plugin-canonical#no-barrel-import It is auto-fixable so you don't need to do much to leverage it and it will prevent bloat of your bundle size. |
Yeah, seems like it helps. I`ve tested with @nextui-org/react and many of internal packages was gone |
For those who have hypothesized that it's a Webpack issue, I cannot reproduce the issue in Webpack alone, treeshaking barrel files seems to work fine there: https://replit.com/@JorenBroekema/barrel-treeshaking In fact, I've also tested this in Rollup and esbuild and they can also treeshake it just fine. Parcel/Browserify not so much So seems like a NextJS specific problem.
|
Hi everyone! Please let us know if https://nextjs.org/docs/app/api-reference/next-config-js/optimizePackageImports resolves your issue with regards to proper tree shaking with Typescript barrel files. If yes, we will proceed with moving this issue to Happy 2024! |
The very idea that we have to manually list libraries that were previously tree-shaken automatically is... not inspiring. |
@ivan-kleshnin Can you share a |
@ivan-kleshnin out of curiosity, why did you thumbs down ESLint plugin I suggested? It is working great for us |
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". |
I'm not the downvoter but couple of reasons:
|
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. |
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 |
@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 |
@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 |
@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! |
Update: there was a fix for the above issue that entailed importing direct Client Components in Server Components. |
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" |
@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. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
The text was updated successfully, but these errors were encountered: