Angular-cli: angular 7: Almost empty AppModule is producing huge main.js (= 367kb)

Created on 22 Sep 2019  ยท  12Comments  ยท  Source: angular/angular-cli

๐Ÿž bug report

I have an app with almost empty AppModule. After ng build --prod I am getting main.js size = 367kb. Expected is around 100kb.

Is this a regression?

No

Description

I have an Angular 7 application consisting of 150+ components. I am trying to reduce first load time of this app by reducing the size ofmain.js. I removed all declarations and imports from AppModuleexcept AppComponentand served the app. This gave me a main.js of size 367kb.
I tried using webpack-bundle-analyser and here is the results for main.js

github

I have been struggling with this issue with the last 10 months. I have wasted countless hours and tried and given up many times. I have posted on Reddit about it but no help. So I am posting it here.

๐Ÿ”ฌ Minimal Reproduction

Git hub repo: https://github.com/goodmite/angular-hello-world-size-too-big
To serve: ng s
visualize using webpack-bundle-analyser: npm run bundle-report

๐ŸŒ Your Environment

Angular Version:


    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / โ–ณ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 7.3.9
Node: 10.16.3
OS: win32 x64
Angular: 7.2.15
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router
... service-worker

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.10.2
@angular-devkit/build-angular     0.10.2
@angular-devkit/build-optimizer   0.10.2
@angular-devkit/build-webpack     0.10.2
@angular-devkit/core              0.7.5
@angular-devkit/schematics        0.7.5
@angular/cdk                      7.3.7
@angular/cli                      7.3.9
@angular/material                 7.3.7
@angular/pwa                      0.7.5
@ngtools/webpack                  7.0.2
@schematics/angular               0.7.5
@schematics/update                0.13.9
rxjs                              6.5.2
typescript                        3.1.6
webpack                           4.19.1

devkibuild-angular triage #1

Most helpful comment

@trotyl
Thanks for your reply. That's a big revelation for me. Can you refer to some doc?

However, don't you see that a bit problematic? Imagine your app having role-based access. Admin is having full access to all app while guest having very limited access. With the current build approach, the guest will also pay for the feature modules only meant for Admin (if those feature module use @angular/common etc).

Edit:
This problem is much severe than it looks.

  1. For me and my team, first load time is most important matric. Our application has role-based access to many roles. Most users (99%) are guest, and they have very limited access. But since HttpModule is using in the admin part of the app, its included main.js and loaded even for guest which doesn't need HttpModule and much such stuff.
  2. This makes bundle analysis very difficult. Imagine using some package, which is using one of these modules. There is no way you can pinpoint that package using webpack-bundle-analyser. All you can see is your main.js is bloated but you can't tell which package caused it.
  3. Even if this is okay for you, shouldn't it be documented very very clearly in the lazy loading doc? Why such important info is missing. And I can tell you this isn't common knowledge in the community. I know that because I asked a related question on SO (....also on reddit, you can see people are clueless and making random guesses), and got no answer/comment. In Addition, I have spent a lot of time making sure I don't import any module to AppModule (which makes life easy but main.js bloated) but feature module. Had I known it previously, I would not be wasting my time.

All 12 comments

On a new prject im seeing a main size of about 245kb
yarn init . && yarn install @angular/cli && npx ng new size && cd size && npx ng build --prod --stats-json && npx webpack-bundle-analyzer dist/size/stats-es2015.json && ls -lh dist/size/

-rw-r--r--   1 fabianwiles  staff   245K 22 Sep 20:40 main-es2015.67b748fec5d4c3ab1a84.js
-rw-r--r--   1 fabianwiles  staff   270K 22 Sep 20:40 main-es5.67b748fec5d4c3ab1a84.js
Angular CLI: 8.3.5
Node: 10.14.2
OS: darwin x64
Angular: 8.2.7
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.803.5
@angular-devkit/build-angular     0.803.5
@angular-devkit/build-optimizer   0.803.5
@angular-devkit/build-webpack     0.803.5
@angular-devkit/core              8.3.5
@angular-devkit/schematics        8.3.5
@angular/cli                      8.3.5
@ngtools/webpack                  8.3.5
@schematics/angular               8.3.5
@schematics/update                0.803.5
rxjs                              6.4.0
typescript                        3.5.3
webpack                           4.39.2

Result in CI:

-rw-r--r-- 1 circleci  15536 Sep 23 04:29 3rdpartylicenses.txt
-rw-r--r-- 1 circleci   4652 Sep 23 04:29 3rdpartylicenses.txt.gz
-rw-r--r-- 1 circleci    948 Sep 23 04:29 favicon.ico
-rw-r--r-- 1 circleci    983 Sep 23 04:29 favicon.ico.gz
-rw-r--r-- 1 circleci    810 Sep 23 04:30 index.html
-rw-r--r-- 1 circleci    405 Sep 23 04:30 index.html.gz
-rw-r--r-- 1 circleci 173212 Sep 23 04:30 main-es2015.63b3a7731cf54ad62923.js
-rw-r--r-- 1 circleci  51214 Sep 23 04:30 main-es2015.63b3a7731cf54ad62923.js.gz
-rw-r--r-- 1 circleci 187901 Sep 23 04:30 main-es5.63b3a7731cf54ad62923.js
-rw-r--r-- 1 circleci  53430 Sep 23 04:30 main-es5.63b3a7731cf54ad62923.js.gz
-rw-r--r-- 1 circleci  37277 Sep 23 04:29 polyfills-es2015.0fe6949bc5ff4b784062.js
-rw-r--r-- 1 circleci  12540 Sep 23 04:29 polyfills-es2015.0fe6949bc5ff4b784062.js.gz
-rw-r--r-- 1 circleci 124853 Sep 23 04:30 polyfills-es5.a83ac866abc867bfd530.js
-rw-r--r-- 1 circleci  42593 Sep 23 04:30 polyfills-es5.a83ac866abc867bfd530.js.gz
-rw-r--r-- 1 circleci   1485 Sep 23 04:29 runtime-es2015.85f895af57b038f1e5b4.js
-rw-r--r-- 1 circleci    758 Sep 23 04:29 runtime-es2015.85f895af57b038f1e5b4.js.gz
-rw-r--r-- 1 circleci   1485 Sep 23 04:29 runtime-es5.85f895af57b038f1e5b4.js
-rw-r--r-- 1 circleci    755 Sep 23 04:29 runtime-es5.85f895af57b038f1e5b4.js.gz
-rw-r--r-- 1 circleci      0 Sep 23 04:29 styles.3ff695c00d717f2d2a11.css
-rw-r--r-- 1 circleci     52 Sep 23 04:29 styles.3ff695c00d717f2d2a11.css.gz

main.js is only 169.2 KB (50.0KB Gzipped).

Unless you can provide a fresh reproduction (has nothing to do with your existing project), it is a support question and should not be opened here.

Hey @trotyl and @Toxicable
I updated the project on github to contain only three modules and removed everything else.
Following is the app Architecture:
gh

As you can see here in TestModule, I have imported 4 modules for demonstation purposes (RouterModule, HttpClientModule, FormsModule, ReactiveFormsModule)

If you comment these 4 modules, the main.js size is 182kb (= 51kb gzip)
If you uncomment these 4 modules, the main.js size is 195kb (= 54.6kb gzip)

Problem:
Since TestModuleis not imported in AppModulein any way, adding or removing modules
in TestModule should have no impact on main.js file size

I updated the project on github to contain only three modules and removed everything else.

@goodmite Instead of patching your project, you should create a newly generated Angular CLI project to reproduce the issue, no one is likely to check your workspace to get out every nuance.

Also, bundling issue should always be reported to Angular CLI.

@trotyl Its a minimal project to reproduce the problem. All 3 modules are almost empty.

But anyway, as per your advice, I have created a fresh angular 8 project and this issue is reproducible too. link is still the same: https://github.com/goodmite/angular-hello-world-size-too-big

Since TestModule is not imported in AppModule in any way, adding or removing modules in TestModule should have no impact on main.js file size

That's totally wrong assumption, code splitting is based on module, not value, so that @angular/core can only exist in one bundle, in this case main.js. Any lazy bundle could increase the non-tree-shakable parts of @angular/core, resulting in bigger @angular/core remaining, thus bigger main.js bundle size.

As long as your lazy module make any additional usage of @angular/core, @angular/common or rxjs, size of main.js would certainly increased, and the stats seems expected so far.

@trotyl
Thanks for your reply. That's a big revelation for me. Can you refer to some doc?

However, don't you see that a bit problematic? Imagine your app having role-based access. Admin is having full access to all app while guest having very limited access. With the current build approach, the guest will also pay for the feature modules only meant for Admin (if those feature module use @angular/common etc).

Edit:
This problem is much severe than it looks.

  1. For me and my team, first load time is most important matric. Our application has role-based access to many roles. Most users (99%) are guest, and they have very limited access. But since HttpModule is using in the admin part of the app, its included main.js and loaded even for guest which doesn't need HttpModule and much such stuff.
  2. This makes bundle analysis very difficult. Imagine using some package, which is using one of these modules. There is no way you can pinpoint that package using webpack-bundle-analyser. All you can see is your main.js is bloated but you can't tell which package caused it.
  3. Even if this is okay for you, shouldn't it be documented very very clearly in the lazy loading doc? Why such important info is missing. And I can tell you this isn't common knowledge in the community. I know that because I asked a related question on SO (....also on reddit, you can see people are clueless and making random guesses), and got no answer/comment. In Addition, I have spent a lot of time making sure I don't import any module to AppModule (which makes life easy but main.js bloated) but feature module. Had I known it previously, I would not be wasting my time.

I'm transferring this issue to the Angular CLI repo, because this is a function of the bundler and tree-shaker, not the core framework.

@trotyl So, you say that if I keep my app.module super clean and if I import e.g third party lib modules only in lazy loaded modules that won't keep my initial bundle size? (main.js) very small?

Since TestModule is not imported in AppModule in any way, adding or removing modules in TestModule should have no impact on main.js file size

That's totally wrong assumption, code splitting is based on module, not value, so that @angular/core can only exist in one bundle, in this case main.js. Any lazy bundle could increase the non-tree-shakable parts of @angular/core, resulting in bigger @angular/core remaining, thus bigger main.js bundle size.

As long as your lazy module make any additional usage of @angular/core, @angular/common or rxjs, size of main.js would certainly increased, and the stats seems expected so far.

That's correct. Splitting a module into smaller chunks is known as "cross module code motion" and afaik only Closure Compiler supports it. Until such feature is supported by Webpack, there's no open source Javascript tooling out there that could perform such advanced optimization.

That said, the explanation above does a great job explaining the current state of JS tooling in Angular CLI, and there's nothing that we could do at the moment to address this, so I'm closing this issue. Thanks.

I have a similar problem. I use lazy loaded modules but my main.js size is big. It contains all the libraries i imported in my package.json file also if i dont use them in app.module but in lazy loaded modules and i dont import those moduels on app.module. Why are they in main.js?

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._

Was this page helpful?
0 / 5 - 0 ratings