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

Tree shaking problem (React Application and Library) #3069

Closed
ghost opened this issue May 25, 2020 · 39 comments · Fixed by #17599
Closed

Tree shaking problem (React Application and Library) #3069

ghost opened this issue May 25, 2020 · 39 comments · Fixed by #17599
Labels
outdated scope: react Issues related to React support for Nx type: bug

Comments

@ghost
Copy link

ghost commented May 25, 2020

hi guys i have problem with tree shaking in nx,

problem with assets library

so i create an application let`s says its (main app) and i create a library called (assets)

index.js (assets library)

export { default as Logo} from './lib/Logo/';
export { default as Loading } from './lib/Loading/';

main.js (main apps)

import { Logo} from '@nx-test/assets';

export const Main= () => {
    return (
        <>
      <Logo/>
        </>
    );
};

export default Main;


nx build main --prod --buildLibsFromSource

when i bundle my main apps in dist/apps/main
i have loading.(hash number).svg so why this loading get bundle?

and when i delete this code
export { default as Loading } from './lib/Loading/';
on my assets index.js
loading.(hash number).svg not get bundle on dist folder

this problem also happen on my ui library and increase a lot of my bundle size

problem with ui library

index.js (ui library )

export { default as MyButton} from './lib/Button/Button';
export { default as MyModal } from './lib/Modal/Modal';

Button.js

import {Button} from 'antd'

export const MyButton =() =>{
    return (
        <Button/>
    }
};

Modal.js

import {Modal} from 'antd'

export const MyModal =() =>{
    return (
        <Modal/>
    }
};

Main.js

import {MyButton} from '@nx-test/ui';

export const Main= () => {
    return (
        <>
      <MyButton/>
        </>
    );
};

export default Main;

as you can see its only import button on main app, but when i remove export Modal on my index.js (entry file ui library) and now its only import button

export { default as MyButton} from './lib/Button/Button';

bundle size decrease 30kb

so why its happen? its like nx keep bundle all the third party (ant design modal) not what im using which is only button

@ghost
Copy link
Author

ghost commented May 25, 2020

run on latest @nrwl/nx 9.3.0

@FrozenPandaz FrozenPandaz added scope: react Issues related to React support for Nx type: bug labels May 25, 2020
@FrozenPandaz
Copy link
Collaborator

Hi, thank you for the details to reproduce. It would help us help you if you went 1 step further and provided a repo we could pull down and see the issue! Could you please provide a repo if you have time?

@ghost
Copy link
Author

ghost commented May 26, 2020

Thanks for your response @FrozenPandaz

i create a repo you can pull it here :
https://github.com/conioX/nx-assets-ui

on this repo :

2 main apps :

main-assets (assets test case)
main-ui (ui test case)

2 libs :

assets
ui

on libs (ui) entry file index.js

export * from './lib/NxButton/NxButton';
//uncomment this below code (NxModal) to test tree shaking
//export * from './lib/NXModal/NXModal';

information

nx build main-ui --prod --buildLibsFromSource
nx build main-assets --prod --buildLibsFromSource

this below image if we commnet nxModal on libs ui entry file index.js.
main.(hash number).esm.js = 76.1kb

2

this below image if we uncommnet nxModal on libs ui entry file index.js.
main.(hash number).esm.js = 95.6kb increase 20kb

1

its like nx bundle 3rd library (ant modal), test on disable cache (chrome)

@montogeek
Copy link
Contributor

@conioX Did you found a solution?

@smakhtin
Copy link

I'm also experience the same problem in my monorepo.

@4kochi
Copy link

4kochi commented Sep 11, 2020

We have the same problem in our nx monorepo. So for example we have an UI lib with many components. Each component has it’s own module file. And all modules are exported through the index.ts file of the lib.

When I now have an app and only import one module from the UI lib, all modules from the lib are included in the bundle after the build, even for the production build.

So one solution is to add path mapping in the root tsconfig.base.json for each module from the lib. Then only the need module is included in the build.

But maybe there is a nicer way? My colleague told me for publishable libs ng-packagr would do the magic. But since we have a monorepo we do not have any publishable lib. It would be nice if there would be some mechanism like for instance in the Angular Material library, where you can also import each module on it’s own to keep the bundle size low.

@dbrody
Copy link

dbrody commented Jan 14, 2021

Any updates on this? Before we begin to utilize a lot more libs in our monorepo which has many advantages, this is a good thing to have clarified so we know how best to architect it. We are using React applications and pure typescript libraries on the latest NX.

@MitkoTschimev
Copy link
Contributor

MitkoTschimev commented Feb 24, 2021

Can agree but we are using angular, we just figured out that we have the same problem.
So I think it is a common problem.
But we found a solution :)

Instead of using the index.ts in the tsconfig.base.json you can add all the index.ts files there

e.g:

"paths": {
      "@myworkspace/shared": ["libs/shared/src/index.ts"],
      ....
}

Change to:

"paths": {
      "@myworkspace/shared": [
           "libs/shared/src/module1/index.ts",
           "libs/shared/src/module2/index.ts",
           "libs/shared/src/module3/index.ts"
      ],
      ....
}

you still can use the import as before.

Try it out ;) for us, it was working.

@adrigardi90
Copy link

Any news about that? We should get that out of the box without any workaround

@m-sanders
Copy link

m-sanders commented Mar 5, 2021

I don’t think this is exclusively an Nx issue but rather a Webpack issue that they’ve tried addressing in Webpack 5:

https://github.com/webpack/changelog-v5#nested-tree-shaking

There’s also a Next.js issue tracking this: vercel/next.js#12557

We’re a little behind in our Nx upgrades so I can’t try webpack 5 just yet, but that would my first port of call. Otherwise it looks like removing re-exports is the naive way to go.

@ZackDeRose
Copy link
Member

Closing this issue as we believe this to be resolved when we upgrade to Webpack 5.

Please reopen if you you believe this is still an issue.

@mcblum
Copy link

mcblum commented Apr 7, 2021

@ZackDeRose can I ask what your webpack config looks like? I can't seem to get this to work. Everything that's exported from index.ts is being imported into my bundle, even if I'm not using it.

@maxime1992
Copy link

@MitkoTschimev I'm facing the exact same issue unfortunately :(

I've tried your solution with great hopes, but it didn't work as it breaks my compilation saying it could find things from the modules.

Example:

Error: ./libs/some-lib/some-effect.effects.ts 50:95-103
"export 'isNotNil' was not found in '@cloudnc/stdlib'
    at HarmonyImportSpecifierDependency._getErrors (/home/maxime/Documents/cloudnc/code/frontend/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:109:11)
    at HarmonyImportSpecifierDependency.getErrors (/home/maxime/Documents/cloudnc/code/frontend/node_modules/webpack/lib/dependencies/HarmonyImportSpecifierDependency.js:68:16)
    at Compilation.reportDependencyErrorsAndWarnings (/home/maxime/Documents/cloudnc/code/frontend/node_modules/webpack/lib/Compilation.js:1463:22)
    at /home/maxime/Documents/cloudnc/code/frontend/node_modules/webpack/lib/Compilation.js:1258:10
    at _next0 (eval at create (/home/maxime/Documents/cloudnc/code/frontend/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:41:1)
    at eval (eval at create (/home/maxime/Documents/cloudnc/code/frontend/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:55:1)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
 @ ./libs/some-lib/some-module.module.ts
 @ ./libs/some-lib/src/index.ts
 @ ./apps/some-app/app.module.ts
 @ ./apps/some-app/main.ts
 @ multi ./apps/some-app/main.ts

Do you have anything more specific in your solution that could help me out? 🙏

@MitkoTschimev
Copy link
Contributor

Hi @maxime1992,

unfortunately it was working for the beginning but as more and more the project was growing with more libs it was not anymore working as in the beginning so we removed them back and waiting for the october release with webpack 5 support.

It is still ok if you cut your libs all in small peaces. So the recommendation is to use domain driven development to keep it them as small as possible and getting a rocket after webpack 5 and tree shaking is doing their job :D

@asit-prakash
Copy link

I migrated my workspace to the latest, but still, I am facing the issue of bundling unused code. Can anyone help?

@ghost
Copy link

ghost commented Dec 7, 2021

Still present in v13.2.3 with Webpack 5
any news?

@asit-prakash
Copy link

asit-prakash commented Dec 9, 2021

I ended up fixing it by modifying my imports. I am not adding entries in index.ts anymore instead directly import the desired component.
example: import Modal from @shared-lib/components/Modal

The drawback is I can't use destructured imports but this solved the bundling issue.

@yuchant
Copy link

yuchant commented Dec 14, 2021

Wow, this is critical to discover late. I assumed importing one item would not involve the whole lib being imported into my next.js project.

@jamosonic
Copy link

We've had luck in webpack 4 with the sideEffects bool in webpack config. Still seems a valid option in webpack 5 https://webpack.js.org/configuration/module/#rulesideeffects

  rules: [
    {
      test: /libpath/,
      sideEffects: false,
    },

This was with a custom app using runCommands tho, so not using any out-of-the-box NX webpack.

@rahul-sharma-uipath
Copy link

rahul-sharma-uipath commented Apr 27, 2022

any update on this? So far I've had no luck with this on v13.3.7 with webpack 5. I've tried the sideEffects: false solution and it appears to have no effect as the production bundle still includes all the modules in the library ):

@asit-prakash
Copy link

any update on this? So far I've had no luck with this on v13.3.7 with webpack 5. I've tried the sideEffects: false solution and it appears to have no effect as the production bundle still includes all the modules in the library ):

What I was doing is have multiple components in shared lib and exporting all of them from index.js and the issue was all of them was getting added in the bundle even if I was using few of them. So the only solution I found was to not export the components from index.js rather import them like import componentName from components/componentName/componentName.tsx where components is my shared lib and inside that i had multiple folder for different components. I know it's not a great approach but this solved my issue.

@rahul-sharma-uipath
Copy link

@asit-prakash I've tried that and it does work but its a hard sell to my colleagues for NX if this is the default workaround. What I'd like to understand is why even though webpack5 does indeed support nested tree shaking https://webpack.js.org/blog/2020-10-10-webpack-5-release/#nested-tree-shaking - it doesn't appear to work for this library. Is there something in the webpack scripts for NX that would cause this?

@mkhib
Copy link

mkhib commented Apr 29, 2022

any update on this? So far I've had no luck with this on v13.3.7 with webpack 5. I've tried the sideEffects: false solution and it appears to have no effect as the production bundle still includes all the modules in the library ):

What I was doing is have multiple components in shared lib and exporting all of them from index.js and the issue was all of them was getting added in the bundle even if I was using few of them. So the only solution I found was to not export the components from index.js rather import them like import componentName from components/componentName/componentName.tsx where components is my shared lib and inside that i had multiple folder for different components. I know it's not a great approach but this solved my issue.

How can I achieve this?
I tried to add the component path into tsconfig.base.json paths; vs code doesn't show any errors on importing the default value but when I run the application it says

Cannot find module or its corresponding type declarations.

@jchamale
Copy link

jchamale commented Jun 9, 2022

This happens to me too

any update on this? So far I've had no luck with this on v13.3.7 with webpack 5. I've tried the sideEffects: false solution and it appears to have no effect as the production bundle still includes all the modules in the library ):

What I was doing is have multiple components in shared lib and exporting all of them from index.js and the issue was all of them was getting added in the bundle even if I was using few of them. So the only solution I found was to not export the components from index.js rather import them like import componentName from components/componentName/componentName.tsx where components is my shared lib and inside that i had multiple folder for different components. I know it's not a great approach but this solved my issue.

How can I achieve this? I tried to add the component path into tsconfig.base.json paths; vs code doesn't show any errors on importing the default value but when I run the application it says

Cannot find module or its corresponding type declarations.

This happens to me too.

@sekarcrazy
Copy link

So the only solution I found was to not export the components from index.js rather import them like import componentName from components/componentName/componentName.tsx

@asit-prakash After I added in tsConfig.base.json file, Getting an error that, component is not exported.
Should I change any other config in libs/shared

@amaralc
Copy link

amaralc commented Jun 20, 2022

I'm in the same situation here.

@amkoehler
Copy link

Not to beat a dead horse, but I'm running into this as well on nx v14.

#3426 was an interesting read on the pros/cons of barrel files. I'm now questioning our use of them, despite their convenience. We have a react component component library with ~25 components exported in the barrel file.

@wolffparkinson
Copy link

Facing the same issue inconvenience of libraries not being tree-shakable inherently.

One way around i am doing is using '*' in paths under tsconfig.base.json

"@org/web/ui-themes/*": [
    "libs/web/ui-themes/src/*"
  ],

This saves you from writing each and every module in path list

@rahul-sharma-uipath
Copy link

@FrozenPandaz and team - this is a pretty big issue with NX, can we get some traction towards a resolution that doesn't rely on workarounds such as the above?

@chrisarts
Copy link

Same problem for me, Im mimgrating from turbo and nx increases the bundle size 10x in my production builds.

@steffbeckers
Copy link

Same problem here, bundle sizes are sky rocketing...

@srbagg
Copy link

srbagg commented Oct 17, 2022

Hey NX team / @FrozenPandaz , kindly re-open the issue as there is no ideal solution found to this problem yet and many clients are reporting the same. Also do we have it on the roadmap? It directly affects performance which is critical to many organizations and needs to be addressed soon.

@rshettyartica
Copy link

This still seems to be an issue. Created a new workspace with a Next.js project, and created a shared UI library with some components. Exporting via index.tsx doesn't support tree shaking.

@seanaguinaga
Copy link

Same issue here

@TheHatSky
Copy link

Same as above.

What I've tried with limited success is using a babel plugin to transform named imports.
The idea is inspired by mui advices on how to minimize bundle size.

Adding these lines to .babelrc files across all our projects has allowed tree-shaking:

{
  // ...
  "plugins": [
    // ...
    [
        // plugin name
        'babel-plugin-import',

        // plugin options
        {
            libraryName: '@finmid/ui',
            libraryDirectory: '',
            camel2DashComponentName: false,
        },

        // plugin instance name, should be unique for each library
        'ui-components',
    ]
  ]
}

In case you use TypeScript, you should add *-paths in your tsconfig.base.json as mentioned above:

{
  "compilerOptions": {
    "paths": {
      "@finmid/ui": ["libs/ui/src/index.ts"], // that's original line
      "@finmid/ui/*": ["libs/ui/src/*"], // <--
    }
  },
}

I don't like this solution:

  • We use TypeScript, and the files transpiled by tsc would differ from what you see in your editor. Which means your whole team should be very consious on how they structure files and exports.
  • This also desyncs your tree-shaked projects from nx workspace. Now you need not to forget keeping .babelrc up-to-date whenever you change or add projects.
  • In order for this to take an effect, this change should be done in each project.

@askides
Copy link

askides commented Nov 23, 2022

Actually experiencing the same problem, does some of you guys found an alternative?

@mnkyjs
Copy link

mnkyjs commented Jan 18, 2023

Can agree but we are using angular, we just figured out that we have the same problem. So I think it is a common problem. But we found a solution :)

Instead of using the index.ts in the tsconfig.base.json you can add all the index.ts files there

e.g:

"paths": {
      "@myworkspace/shared": ["libs/shared/src/index.ts"],
      ....
}

Change to:

"paths": {
      "@myworkspace/shared": [
           "libs/shared/src/module1/index.ts",
           "libs/shared/src/module2/index.ts",
           "libs/shared/src/module3/index.ts"
      ],
      ....
}

you still can use the import as before.

Try it out ;) for us, it was working.

Is this still a valid workaround? I tried to implement it today, but the imports are then partly nicjht more found.

What I try to export are not only modules, but also e.g. models, which then have their own entrypoint (index.ts). @MitkoTschimev

@Jasonkoolman
Copy link

Jasonkoolman commented Feb 9, 2023

I've found a potential fix. See this answer which might resolve the issue of Webpack not properly tree shaking barrel files

@github-actions
Copy link

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated scope: react Issues related to React support for Nx type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.