You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Increased main.js bundle size but less lazy chunks, another case with Angular v9-rc.11 in a monorepo structure聽#16799
Yes, with Angular v8, or Ivy disabled, the main bundle size issue did not happen
Description
Like in issue #16146, Ivy enabled builds result in bigger main bundles.
We use an app-shell plus lazy load of components- with Angular Elements like in aio, and routes. With View Engine (VE), both in Angular 8 and 9, the main bundle effectively only included what was required to render the shell:
Core framework,
Auth back-end,
Angular Material sidenav and toolbar,
Shell components.
Once Ivy is enabled, it includes additional code. E.g., in one application, we get those two source-map-explorer results:
In other app, the result is also bigger main bundles. In particular, it adds all the @Injectable({providedIn: 'root}) services that are provided in a data-access internal library:
馃敩 Minimal Reproduction
The issue is in a proprietary mono-repo of front-ends. We can provide access in Bitbucket or Github private repositories if necessary. To generate the bundles, we used
ng build <project-name> --prod --source-map --localize=false
And only changed the tsconfig.json to set angularCompilerOptions.enableIvy = false
I also don't understand why I see so many of these warnings in the ivy compilation:
WARNING in D:\sandbox\bmat-front-end-monorepo-angular\apps\reporting-tool\src\app\feature-lazy-channel-list\user-channel-tree\user-channel-tree.component.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
WARNING in D:\sandbox\bmat-front-end-monorepo-angular\apps\reporting-tool\src\app\feature-lazy-channel-list\lazy-channel-list.module.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
WARNING in D:\sandbox\bmat-front-end-monorepo-angular\apps\reporting-tool\src\app\feature-lazy-side-menu\side-menu\side-menu.component.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
WARNING in D:\sandbox\bmat-front-end-monorepo-angular\apps\reporting-tool\src\app\feature-lazy-side-menu\lazy-side-menu.module.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
WARNING in D:\sandbox\bmat-front-end-monorepo-angular\libs\auth\feature-angular-auth-user-apps\src\lib\feature-angular-auth-user-apps.component.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
WARNING in D:\sandbox\bmat-front-end-monorepo-angular\libs\auth\feature-angular-auth-user-apps\src\lib\component\app-list-icon\app-list-icon.component.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
This shouldn't happen because the tsconfig for this build contains explicit entry points:
Removing that include removes the warnings, but doesn't affect file size.
Double checked my assertion that @bmat/reporting-tool/reporting-tool-interfaces was only found in a single file and found that to be false, I must have used a bad search before. It's found in loads of files, including a lot in libs/reporting-tool/data-access-reporting-tool-api. That is why it gets included.
Reacted by Michael Prentice, Piotr Lewandowski and marko164
Tried completely removing all imports from libs/reporting-tool/data-access-reporting-tool-api/src/index.ts to see if the presence of more imports there made a difference. The main bundle gets 1kb smaller and sourcemaps show the only things from libs/reporting-tool/data-access-reporting-tool-api/src/ are the really imported ones (libs/reporting-tool/data-access-reporting-tool-api/src/lib/reporting-tool-data-access-reporting-tool-api.module.ts).
Although this was just a 1kb difference, there's something odd here. Importing a re-export from a index file shouldn't cause some unrelated files to stay in the compilation.
Knowing this, I went back to libs/reporting-tool/reporting-tool-interfaces/src/lib/production-category-models.ts, which was included because of libs/reporting-tool/reporting-tool-interfaces/src/index.ts. I still don't know what's importing that last one. But I'm pretty sure that if I comment it's contents out, the TS compiler will error out and tell me.
Sure enough, it points me towards libs/search-filters/feature-custom-filters/src/lib/filters-dialogs-list-item/filters-dialogs-list-item.component.ts, which imports two enums that are in libs/reporting-tool/reporting-tool-interfaces/src/lib/reporting-tool-reporting-tool-interfaces.ts, and re-exported through said index.ts.
I comment out everything in libs/reporting-tool/reporting-tool-interfaces/src/index.ts except the export that's actually used, and again main drops in size, this time by ~60kb. It's still not on par with the VE main file, but it's getting closer.
So there's definitely something going on with re-exports. In some causes they get retained in Ivy, but not in VE.
src/app/app.module.ts imports AnotherModule from src/app/another/index.ts
src/app/another/index.ts also exports src/app/another/side-effects.ts, a file that isn't used but loads a library (Luxon) and has a toplevel side effect (a Map instantiated with ['key', 'value']).
npm run repro will build the application first with Ivy, and then with View Engine (VE, the compiler before Ivy).
The Ivy build will contain the contents of src/app/another/side-effects.ts, but the VE build will not.
You can tell because the Ivy build is much larger. You can also open dist/barrel-side-effects/main.js and find Luxon referenced there, along with map (as new Map([["key","value"]])).
Reacted by Cristian, Michael Prentice, Dmitry A. Efimenko, Dean Davidson, marko164 and Maik
Great, thanks for your deep analysis and minimal repo. I'll take a look; I'm not used to ngcc and build tool internals but anyway will try to see if I can help with this issue.
I'm not really sure what can be done about this, as it was a design choice. VE used deep imports because of the whole separate factory file business, and we also ended up removing unused imports after the decorators were stripped out of the original file, so index file were bypasses. But Ivy just uses the same file instead, so whatever imports are there, stay there.
It's not strictly incorrect mind you. VE was doing something incorrect, insofar as any side effects or imports you had referenced in files re-exported from index files could be dropped. But your app might rely on them, so that wasn't right. Ivy does the right thing and doesn't mess with your import structure.
But in cases like this, where there's a lot of shared code and the shared libraries are not free from side effects, extra code ends up being retained. You could hypothetically go around and use deep imports everywhere, but that's not great.
There's something else you can do though. If your shared libraries are meant to be free from side effects, add "sideEffects": false to their package.json. You can also just drop a new package.json with {"sideEffects": false} on folders that you know are free from side effects, files in that folder and in any subfolder will be treated as such.
The more I think about it, the more I that's the right thing to do in this case. My reasoning is this: VE erroneously caused some index files to be treated as though they had no side effects and left you no recourse, but Ivy doesn't do this and instead requires you to mark code free from side effects as such.
For most of our applications is not a big issue to have larger main bundles; those are b2b web application with service workers, hence it's fine to once in while fetch more data.
Also, I noticed that patching the package.json of Luxon, or the Webpack configuration webpackConfig.resolve.mainFields of Angular CLI, to use Luxon ESM instead of CJS modules resulted in solving the issue for the reproduction repository. If the change in the behaviour of Ivy is intended and the correct one, we can see this issue as a bundler + app architecture problem and not an Angular one.
Maybe we can add somewhere a mention to this change and why it's the correct behaviour compared to VE. It will save other users from opening issues like this one and may help them into finding ways to improve their bundle sizes.
Again, thanks a lot for your deep review and comments.
Reacted by Igor Minar, Filipe Silva, Michael Prentice and marko164
There's something else you can do though. If your shared libraries are meant to be free from side effects, add "sideEffects": false to their package.json. You can also just drop a new package.json with {"sideEffects": false} on folders that you know are free from side effects, files in that folder and in any subfolder will be treated as such.
The shared libraries I meant where the ones in your libs/ folder.
It's not safe to patch the package.json of Luxon, because we don't know if it actually is free from side effects or not. It would be safe to use its ESM entry point (module in package.json), but we prioritize browser instead because often there are libraries that primarily have Node entry points and then put a browser entry point in browser.
Yes, all was understood. The patch was an experiment to set the ESM entry point instead of CJS one, not an actual code change nor sideEffects = false declaration, and see what happened.
For my part, and considering that the Ivy behaviour is more reasonable, this issue can be closed. My only suggestion is, perhaps, explain this new treatment of the side effects in the https://next.angular.io/guide/ivy page.
Thanks,
Cristian
Reacted by Rafael Kraut, Filipe Silva and Michael Prentice
so does this mean that angular/forms should be marked as sideEffects = false?
In my particular case, @angular/forms was dragged to the main bundle by a custom library that provided a date/month picker and imported it. Once I checked that our own libraries were free from side effects, and marked it with module level package.json, it returned to similar a size and identical contents.
We didn't patch any third party library, nor @angular packages to achieve it, and I wouldn't take that path unless strictly necessary.
The @angular/forms package is currently marked as side effect free. All the client-side @angular packages are marked with the exception of the @angular/compiler package which is not used in a production (AOT compiled) application.
Activity
IgorMinar commentedon Jan 30, 2020
@angular/material is the most curious piece - it looks like with Ivy all of it is being retained in these apps. That's highly unusual.
Is there any chance we could follow up offline and get a debug version of the bundle for us to analyze?
Is there anything else atypical about your setup?
9 remaining items
filipesilva commentedon Jan 31, 2020
I also don't understand why I see so many of these warnings in the ivy compilation:
This shouldn't happen because the tsconfig for this build contains explicit entry points:
Ah but this one extends another one that actually has a broad include:
Removing that include removes the warnings, but doesn't affect file size.
Double checked my assertion that
@bmat/reporting-tool/reporting-tool-interfaceswas only found in a single file and found that to be false, I must have used a bad search before. It's found in loads of files, including a lot inlibs/reporting-tool/data-access-reporting-tool-api. That is why it gets included.filipesilva commentedon Jan 31, 2020
Tried completely removing all imports from
libs/reporting-tool/data-access-reporting-tool-api/src/index.tsto see if the presence of more imports there made a difference. The main bundle gets 1kb smaller and sourcemaps show the only things fromlibs/reporting-tool/data-access-reporting-tool-api/src/are the really imported ones (libs/reporting-tool/data-access-reporting-tool-api/src/lib/reporting-tool-data-access-reporting-tool-api.module.ts).Although this was just a 1kb difference, there's something odd here. Importing a re-export from a index file shouldn't cause some unrelated files to stay in the compilation.
Knowing this, I went back to
libs/reporting-tool/reporting-tool-interfaces/src/lib/production-category-models.ts, which was included because oflibs/reporting-tool/reporting-tool-interfaces/src/index.ts. I still don't know what's importing that last one. But I'm pretty sure that if I comment it's contents out, the TS compiler will error out and tell me.Sure enough, it points me towards
libs/search-filters/feature-custom-filters/src/lib/filters-dialogs-list-item/filters-dialogs-list-item.component.ts, which imports two enums that are inlibs/reporting-tool/reporting-tool-interfaces/src/lib/reporting-tool-reporting-tool-interfaces.ts, and re-exported through saidindex.ts.I comment out everything in
libs/reporting-tool/reporting-tool-interfaces/src/index.tsexcept the export that's actually used, and again main drops in size, this time by ~60kb. It's still not on par with the VE main file, but it's getting closer.So there's definitely something going on with re-exports. In some causes they get retained in Ivy, but not in VE.
filipesilva commentedon Jan 31, 2020
Ok, I was able to destil this down to a minimal repro:
BarrelSideEffects
This repository reproduces the problem in #16799.
What's happening?
This app contains the following files:
src/app/another/index.tssrc/app/another/another.module.tssrc/app/another/side-effects.tssrc/app/app.module.tsThese files are setup so that:
src/app/app.module.tsimportsAnotherModulefromsrc/app/another/index.tssrc/app/another/index.tsalso exportssrc/app/another/side-effects.ts, a file that isn't used but loads a library (Luxon) and has a toplevel side effect (aMapinstantiated with['key', 'value']).npm run reprowill build the application first with Ivy, and then with View Engine (VE, the compiler before Ivy).The Ivy build will contain the contents of
src/app/another/side-effects.ts, but the VE build will not.You can tell because the Ivy build is much larger. You can also open
dist/barrel-side-effects/main.jsand findLuxonreferenced there, along with map (asnew Map([["key","value"]])).dogmatico commentedon Feb 1, 2020
Great, thanks for your deep analysis and minimal repo. I'll take a look; I'm not used to ngcc and build tool internals but anyway will try to see if I can help with this issue.
filipesilva commentedon Feb 1, 2020
I'm not really sure what can be done about this, as it was a design choice. VE used deep imports because of the whole separate factory file business, and we also ended up removing unused imports after the decorators were stripped out of the original file, so index file were bypasses. But Ivy just uses the same file instead, so whatever imports are there, stay there.
It's not strictly incorrect mind you. VE was doing something incorrect, insofar as any side effects or imports you had referenced in files re-exported from index files could be dropped. But your app might rely on them, so that wasn't right. Ivy does the right thing and doesn't mess with your import structure.
But in cases like this, where there's a lot of shared code and the shared libraries are not free from side effects, extra code ends up being retained. You could hypothetically go around and use deep imports everywhere, but that's not great.
There's something else you can do though. If your shared libraries are meant to be free from side effects, add
"sideEffects": falseto theirpackage.json. You can also just drop a newpackage.jsonwith{"sideEffects": false}on folders that you know are free from side effects, files in that folder and in any subfolder will be treated as such.The more I think about it, the more I that's the right thing to do in this case. My reasoning is this: VE erroneously caused some index files to be treated as though they had no side effects and left you no recourse, but Ivy doesn't do this and instead requires you to mark code free from side effects as such.
dogmatico commentedon Feb 3, 2020
For most of our applications is not a big issue to have larger main bundles; those are b2b web application with service workers, hence it's fine to once in while fetch more data.
Also, I noticed that patching the package.json of Luxon, or the Webpack configuration
webpackConfig.resolve.mainFieldsof Angular CLI, to use Luxon ESM instead of CJS modules resulted in solving the issue for the reproduction repository. If the change in the behaviour of Ivy is intended and the correct one, we can see this issue as a bundler + app architecture problem and not an Angular one.Maybe we can add somewhere a mention to this change and why it's the correct behaviour compared to VE. It will save other users from opening issues like this one and may help them into finding ways to improve their bundle sizes.
Again, thanks a lot for your deep review and comments.
filipesilva commentedon Feb 3, 2020
Just to be clear, when I said
The shared libraries I meant where the ones in your
libs/folder.It's not safe to patch the
package.jsonof Luxon, because we don't know if it actually is free from side effects or not. It would be safe to use its ESM entry point (modulein package.json), but we prioritizebrowserinstead because often there are libraries that primarily have Node entry points and then put a browser entry point inbrowser.dogmatico commentedon Feb 3, 2020
Yes, all was understood. The patch was an experiment to set the ESM entry point instead of CJS one, not an actual code change nor sideEffects = false declaration, and see what happened.
For my part, and considering that the Ivy behaviour is more reasonable, this issue can be closed. My only suggestion is, perhaps, explain this new treatment of the side effects in the https://next.angular.io/guide/ivy page.
Thanks,
Cristian
DmitryEfimenko commentedon Feb 5, 2020
so does this mean that angular/forms should be marked as
sideEffects = false?dogmatico commentedon Feb 5, 2020
In my particular case,
@angular/formswas dragged to the main bundle by a custom library that provided a date/month picker and imported it. Once I checked that our own libraries were free from side effects, and marked it with module levelpackage.json, it returned to similar a size and identical contents.We didn't patch any third party library, nor
@angularpackages to achieve it, and I wouldn't take that path unless strictly necessary.clydin commentedon Feb 5, 2020
The
@angular/formspackage is currently marked as side effect free. All the client-side@angularpackages are marked with the exception of the@angular/compilerpackage which is not used in a production (AOT compiled) application.filipesilva commentedon Feb 6, 2020
We've added documentation about this class of problem in angular/angular#35178. It should be available https://next.angular.io/guide/ivy-compatibility#payload-size-debugging now, and shortly in https://angular.io/guide/ivy-compatibility#payload-size-debugging
angular-automatic-lock-bot commentedon Mar 8, 2020
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.