{"id":2439,"date":"2017-06-03T11:19:32","date_gmt":"2017-06-03T09:19:32","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=2439"},"modified":"2017-06-03T11:19:32","modified_gmt":"2017-06-03T09:19:32","slug":"shrinking-angular-bundles-with-closure","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/","title":{"rendered":"Shrinking Angular Bundles With Closure"},"content":{"rendered":"<div class=\"article\">\n<blockquote><p>\nBig thanks to <a href=\"https:\/\/twitter.com\/Jakeherringbone\">Alex Eagle<\/a> from the Angular Team and to <a href=\"https:\/\/twitter.com\/CarmenPopoviciu\">Carmen Popoviciu<\/a> for reviewing this post.\n<\/p><\/blockquote>\n<p>Closure is said to be the most sophisticated JavaScript compiler available today. Its advanced optimization mode goes far beyond the tree shaking capabilities of other tools and allows for shrinking bundles to a minimum. Google uses it to improve the performance of its own products like Google Docs and even Microsoft is using it meanwhile for Office 365. However, it's considered to be an expert tool and therefore difficult to configure. In addition to that, it assumes that the underlying JavaScript code has been written in a specific way.<\/p>\n<p>Currently, the Angular team is working hard on making <a href=\"https:\/\/medium.com\/@Jakeherringbone\/what-angular-is-doing-with-bazel-and-closure-21f526f64a34\">Angular work together with Closure<\/a> as well as with its build tool Bazel. There are some first examples available, e. g. the <a href=\"https:\/\/github.com\/angular\/closure-demo\">Example<\/a> <a href=\"https:\/\/twitter.com\/jakeherringbone\">Alex Eagle<\/a> from the Angular Team created.<\/p>\n<p>This post uses the mentioned example to show how to use the Closure compiler as well as the advantages it brings regarding bundle sizes. Furthermore, this post explains how to add own and existing packages to a Closure based project.<\/p>\n<h2>Building a base line with the Angular CLI<\/h2>\n<p>In order to create a baseline for comparing Closure with a 'traditional' build task for Angular, let's create a new Hello World application with the <a href=\"https:\/\/cli.angular.io\">Angular CLI<\/a>:<\/p>\n<pre><code>ng new baseline\ncd baseline\n<\/code><\/pre>\n<p>Now, let's create a production build:<\/p>\n<pre><code>ng build --prod\n<\/code><\/pre>\n<p>The generated bundles have a size of about 394K:<\/p>\n<pre><code>   1.460 inline.093de888567e5146835d.bundle.js\n   9.360 main.0d097609144c942cc763.bundle.js\n  60.845 polyfills.d90888e283bda7f009a0.bundle.js\n 322.320 vendor.765bef7fc0b73d2d51d7.bundle.js\n\n         393.985 Bytes\n<\/code><\/pre>\n<p>As the Closure sample in the next sections is directly importing the zone.js polyfill and not importing any other ones, we should omit the polyfills bundle from this observation:<\/p>\n<pre><code>   1.460 inline.093de888567e5146835d.bundle.js\n   9.360 main.0d097609144c942cc763.bundle.js\n 322.320 vendor.765bef7fc0b73d2d51d7.bundle.js\n\n         333.140 Bytes\n<\/code><\/pre>\n<p>This leaves about 333K.<\/p>\n<p>After this we are installing Angular Material as well as the Animation package which Angular Material depends on:<\/p>\n<pre><code>npm i @angular\/material --save\nnpm i @angular\/animations --save\n<\/code><\/pre>\n<p>To import it into the application, its <code>AppModule<\/code> is referencing some of Angular Material's Modules:<\/p>\n<pre><code>import { BrowserModule } from '@angular\/platform-browser';\nimport { NgModule } from '@angular\/core';\nimport { FormsModule } from '@angular\/forms';\nimport { HttpModule } from '@angular\/http';\nimport { AppComponent } from '.\/app.component';\n\nimport { \n  MdButtonModule, \n  MdAutocompleteModule,\n  MdCheckboxModule,\n  MdDatepickerModule,\n  MdCardModule,\n  MdRadioModule,\n  MdChipsModule,\n  MdListModule,\n  MdSnackBarModule,\n  MdSliderModule,\n  MdDialogModule,\n  MdMenuModule,\n  MdSidenavModule\n} from '@angular\/material';\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    FormsModule,\n    HttpModule,\n    MdButtonModule, \n    MdAutocompleteModule,\n    MdCheckboxModule,\n    MdDatepickerModule,\n    MdCardModule,\n    MdRadioModule,\n    MdChipsModule,\n    MdListModule,\n    MdSnackBarModule,\n    MdSliderModule,\n    MdDialogModule,\n    MdMenuModule,\n    MdSidenavModule\n  ],\n  declarations: [\n    AppComponent\n  ],\n  providers: [],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }\n<\/code><\/pre>\n<p>After recreating a production build (<code>ng build --prod<\/code>) we see that it grew to about 951K:<\/p>\n<pre><code>   1.460 inline.36030f130bb8b4d4d1e6.bundle.js\n 246.959 main.bd55167e3a85bc1edaab.bundle.js\n 702.496 vendor.9618c29d7fbb7af5a536.bundle.js\n      950.915 Bytes\n<\/code><\/pre>\n<p>As the bundle size increased even though we are not using the imported modules from Angular Material this shows that the CLI\/ webpack is not able to find this out and shake these modules off.<\/p>\n<p>Now let's see how the sample application can be optimized with Closure.<\/p>\n<h2>Using the Closure Compiler<\/h2>\n<p>To get started with Angular and the Closure Compiler, I'm using <a href=\"https:\/\/twitter.com\/jakeherringbone?lang=de\">Alex Eagle<\/a>'s example. For this, I've <a href=\"https:\/\/github.com\/manfredsteyer\/closure-demo\">forked<\/a> the version available when writing this.<\/p>\n<p>I modified it to use npm instead of yarn and after running <code>npm run build<\/code> I got a bundle with just about 106K:<\/p>\n<pre><code> 105.934 bundle.js\n<\/code><\/pre>\n<p>This is an huge improvement over using the CLI with webpack which led to bundles with about 390K for a quite similar \"Hello-Word\"-App.<\/p>\n<p>In order to make a fair comparison, we also need to include the packages <code>@angular\/forms<\/code> and <code>@angular\/http<\/code> as these packages are also imported into the CLI based app:<\/p>\n<pre><code>npm i @angular\/http --save\nnpm i @angular\/forms --save\n\n<\/code><\/pre>\n<p>In order to import them into the Angular App, the <code>AppModule<\/code> has to reference them:<\/p>\n<pre><code>import {NgModule} from '@angular\/core';\nimport {HttpModule} from '@angular\/http';\nimport {FormsModule} from '@angular\/forms';\nimport {BrowserModule} from '@angular\/platform-browser';\nimport {Basic} from '.\/basic';\n\n@NgModule({\n  declarations: [Basic],\n  bootstrap: [Basic],\n  imports: [BrowserModule, FormsModule, HttpModule],\n})\nexport class AppModule {\n}\n<\/code><\/pre>\n<p>Unfortunately, the Closure Compiler does not respect the conventions introduced by NodeJS and so it does not automatically search the folder <code>node_modules<\/code> for these packages. Therefore, they have to be referenced explicitly within the file <code>closure.conf<\/code>:<\/p>\n<pre><code>node_modules\/@angular\/forms\/@angular\/forms.js\n--js_module_root=node_modules\/@angular\/forms\n\nnode_modules\/@angular\/http\/@angular\/http.js\n--js_module_root=node_modules\/@angular\/http\n<\/code><\/pre>\n<p>Those lines reference both, the path where the package is found as well as its entry point which is also the whole content of the package due do the use of the <a href=\"https:\/\/docs.google.com\/document\/d\/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs\/edit\">FESM15-Format<\/a>.<\/p>\n<p>After building everything again (<code>npm run build<\/code>) we are ending up with about 125K:<\/p>\n<pre><code> 125.134 bundle.js\n<\/code><\/pre>\n<p>This is still an huge improvement over using the CLI and\/or Webpack.<\/p>\n<blockquote><p>\nPlease note that this example is using Closure's Advanced Mode. This mode provides better results as other known tools, but is also quite aggressive. That's why it can damage the generated bundle and so it's recommended to use this mode always together with good E2E testing.\n<\/p><\/blockquote>\n<h2>Using Closure with an Angular Package through the example of Angular Material<\/h2>\n<p>Now let's go on and import Angular Material. Once again, we have to load the following packages:<\/p>\n<pre><code>npm i @angular\/material --save\nnpm i @angular\/animations --save\n<\/code><\/pre>\n<p>And - of course - we have to import the same Angular Material Modules as before:<\/p>\n<pre><code>import {NgModule} from '@angular\/core';\nimport {BrowserModule} from '@angular\/platform-browser';\nimport {Basic} from '.\/basic';\nimport { LibStarterModule } from 'stuff-lib';\nimport { HttpModule } from \"@angular\/http\";\nimport { FormsModule } from \"@angular\/forms\";\n\nimport { \n  MdButtonModule, \n  MdAutocompleteModule,\n  MdCheckboxModule,\n  MdDatepickerModule,\n  MdCardModule,\n  MdRadioModule,\n  MdChipsModule,\n  MdListModule,\n  MdSnackBarModule,\n  MdSliderModule,\n  MdDialogModule,\n  MdMenuModule,\n  MdSidenavModule\n} from '@angular\/material';\n\n@NgModule({\n  declarations: [Basic],\n  bootstrap: [Basic],\n  imports: [\n    BrowserModule, \n    HttpModule, \n    FormsModule, \n    MdButtonModule, \n    MdAutocompleteModule,\n    MdCheckboxModule,\n    MdDatepickerModule,\n    MdCardModule,\n    MdRadioModule,\n    MdChipsModule,\n    MdListModule,\n    MdSnackBarModule,\n    MdSliderModule,\n    MdDialogModule,\n    MdMenuModule,\n    MdSidenavModule\n  ],\n})\nexport class AppModule {\n}\n\n<\/code><\/pre>\n<p>In addition to that, it is also necessary to tell Closure about the imported modules. Therefore the <code>closure.conf<\/code> gets the following additional lines:<\/p>\n<pre><code>node_modules\/@angular\/animations\/@angular\/animations.js\n--js_module_root=node_modules\/@angular\/animations\n\nnode_modules\/@angular\/material\/@angular\/material.js\n--js_module_root=node_modules\/@angular\/material\n<\/code><\/pre>\n<p>Before we can create a build, we have to update the TypeScript Compiler, as the used version of the example comes with 2.1 while the current version of Angular Material needs 2.2+:<\/p>\n<pre><code>npm uninstall typescript --save-dev\nnpm install typescript@^2.2 --save-dev\n<\/code><\/pre>\n<p>After creating another build our bundles has about 200K:<\/p>\n<pre><code>199.970 bundle.js\n<\/code><\/pre>\n<p>This shows two things: First of all, using Closure brings a huge improvement over using the CLI and\/or webpack which led to a bundle with about 951K. But this experiment also shows that even Closure is not able to fully shake off the imported but unused Modules.<\/p>\n<h2>Creating an own Angular Package that can be used with Closure<\/h2>\n<p>Creating an own Angular Package that can be used together with the closure compiler is quite easy. You just need to align with the conventions for the <a href=\"https:\/\/docs.google.com\/document\/d\/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs\/edit\">Angular Package Format<\/a>. The most important thing to consider for Closure is providing a build using the FESM15 format. This means that you have to use EcmaScript 2015+ with EcmaScript Modules. Furthermore, you also have to \"flatten\" everything into one file which is used as the package's entry file. The Angular Package Format also tells us to provide your code in other formats, but here I'm just focusing on FESM15.<\/p>\n<p>For testing purposes, I've created such a package with some demo code. It's called <a href=\"https:\/\/github.com\/manfredsteyer\/angular-stuff-lib\">angular-stuff-lib<\/a> and just contains a simple Angular Module with some a <code>DemoComponent<\/code>.<\/p>\n<pre><code>@NgModule({\n    imports: [\n        CommonModule,\n        FormsModule,\n        HttpModule\n    ],\n    declarations: [\n        DemoComponent\n    ],\n    exports: [\n        DemoComponent\n    ]\n})\nexport class LibStarterModule {\n\n    static forRoot(): ModuleWithProviders {\n        return {\n            ngModule: LibStarterModule,\n            providers: [\n                DemoService\n            ]\n        };\n    }\n\n}\n<\/code><\/pre>\n<p>Its <code>forRoot<\/code> method returns this module with a <code>DemoService<\/code>. Using this method you can make sure that the service is only registered with your application's <code>RootModule<\/code> and not with other modules that import this one.<\/p>\n<p>The package uses the following <code>tsconfig.json<\/code>:<\/p>\n<pre><code>{\n  \"compilerOptions\": {\n    \"module\": \"es2015\",\n    \"target\": \"es2015\",\n    \"outDir\": \"build\",\n    \"noImplicitAny\": true,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"declaration\": true,\n    \"moduleResolution\": \"node\",\n    \"noUnusedLocals\": true,\n    \"types\": [\n      \"hammerjs\",\n      \"jasmine\",\n      \"node\"\n    ],\n    \"lib\": [\"es2015\", \"dom\"]\n  },\n  \"files\": [\n    \"index.ts\"\n  ],\n  \"angularCompilerOptions\": {\n    \"strictMetadataEmit\": true,\n    \"skipTemplateCodegen\": true,\n    \"annotationsAs\": \"static fields\",\n    \"annotateForClosureCompiler\": true,\n    \"flatModuleOutFile\": \"stuff-lib.js\",\n    \"flatModuleId\": \"stuff-lib\"\n  }\n}\n<\/code><\/pre>\n<p>Please note several things here:<\/p>\n<ul>\n<li>The compilation target is EcmaScript 2015 with EcmaScript (2015) Modules.<\/li>\n<li>The destination folder is called <code>built<\/code>.<\/li>\n<li>The configuration directly points to the main file <code>index.ts<\/code>.<\/li>\n<li>Thanks to the property <code>declaration<\/code>, the TypeScript compiler emits Type Declarations. This allows the compiled EcmaScript-based package to be used within TypeScript projects.<\/li>\n<li>There are some options for the Angular Compiler in  <code>angularCompilerOptions<\/code>:\n<ul>\n<li><code>strictMetadataEmit<\/code> creates some meta data the Angular Compiler needs to produce an AOT build for projects that use this package.<\/li>\n<li><code>skipTemplateCodegen<\/code> is set to true because we don't need compiled template for a library. The final project will create all of them.<\/li>\n<li><code>annotateForClosureCompiler<\/code> makes the Angular Compiler to generate comments with annotations Closure uses to optimize the emitted code.<\/li>\n<li><code>flatModuleOutFile<\/code> points to a file the Angular Compiler creates as an entry point. It can be used by tools like <code>rollup<\/code> to create a flat package (a package that just consists of one file).<\/li>\n<li><code>flatModuleId<\/code> contains the module name of the generated package. This is the name that can be used together with <code>import<\/code> statements.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>To create the build, I'm using some npm scripts:<\/p>\n<pre><code>\"scripts\": {\n    \"build\": \"npm run clear &amp;&amp; npm run ngc &amp;&amp; npm run rollup &amp;&amp; npm run copy\",\n    \"clear\": \"rimraf build &amp;&amp; rimraf dist\",\n    \"ngc\": \"ngc\",\n    \"rollup\" : \"rollup build\/stuff-lib.js -o dist\/stuff-lib.js\",\n    \"copy\": \"npm run copy-package &amp;&amp; npm run copy-metadata &amp;&amp; npm run copy-typedef\",\n    \"copy-typedef\": \"cd build &amp;&amp; cpy **\/*.d.ts ..\/dist --parents\",\n    \"copy-metadata\": \"cd build &amp;&amp; cpy **\/*.metadata.json ..\/dist\",\n    \"copy-package\": \"cpy dist-package.json dist\/package.json\"\n}\n<\/code><\/pre>\n<p>The script <code>build<\/code> is triggering the whole procedure (<code>npm run build<\/code>). First of all, it clears the folders that are used for the compilation targets by leveraging <code>rimraf<\/code>. Then it starts the Angular Compiler <code>ngc<\/code> which is also using the TypeScript Compiler underneath. The results of this compilation step can be found within the folder <code>build<\/code> after this. Then, the tool rollup generates the flat package file and puts it into the folder <code>dist<\/code>. To make sure that all other files needed are located within <code>dist<\/code> the those files are copied there. It also copies the <code>package.json<\/code> which contains necessary metadata for this package to the <code>dist<\/code> folder.<\/p>\n<p>Among others, those meta data contains the following entries:<\/p>\n<pre><code>  \"name\": \"stuff-lib\",\n  [...]\n  \"module\": \"stuff-lib.js\",\n  \"es2015\": \"stuff-lib.js\",\n  \"typings\": \"stuff-lib.d.ts\",\n<\/code><\/pre>\n<p>The property <code>name<\/code> contains the name of the package and <code>es2015<\/code> is pointing to the generated flat ES2015 bundle. Normally, <code>module<\/code> would point to its ES5 counterpart. But as I'm focusing only on ES2015 here, it is pointing to the generated ES2015 bundle too. In addition to this, <code>typings<\/code> is pointing to the entry file of the emitted type definitions.<\/p>\n<p>One last thing that isn't specific to Closure or the Angular Module Format but is important nevertheless: To avoid that Angular is installed as a sub dependency when one is downloading this package, Angular is only mentioned within <code>peerDependencies<\/code>. This tells npm that everyone who is installing this package, should also install those packages.<\/p>\n<pre><code>\"peerDependencies\": {\n  \"@angular\/core\": \"^4.0.0\",\n  \"@angular\/http\": \"^4.0.0\"\n},\n<\/code><\/pre>\n<p>To build this project, just run <code>npm run build<\/code>. After this, you find the compiled package within the folder <code>dist<\/code>.<\/p>\n<h2>Using the own package together with Closure<\/h2>\n<p>To test your own Angular package with the Closure Compiler, switch to the <code>dist<\/code> folder after building it and call <code>npm link<\/code>. Then switch to the root folder of the Closure project and run <code>npm link stuff-lib<\/code> which is creating a symbolic link to your package's <code>dist<\/code> folder.<\/p>\n<p>After this, you have to tell Closure about the added package by inserting some lines into the file <code>closure.conf<\/code>:<\/p>\n<pre><code>node_modules\/stuff-lib\/stuff-lib.js\n--js_module_root=node_modules\/stuff-lib\n<\/code><\/pre>\n<p>As mentioned above, the line with <code>--js_module_root<\/code> is pointing to the package's root directory located within <code>node_modules<\/code>. This is necessary because Closure doesn't follow the conventions introduced by Node. The other line points to the flat bundle which is the entry point of the package and contains its whole contents.<\/p>\n<p>After this, just import the package's module into the <code>AppModule<\/code>:<\/p>\n<pre><code>import { NgModule } from '@angular\/core';\nimport { BrowserModule } from '@angular\/platform-browser';\nimport { Basic } from '.\/basic';\nimport { LibStarterModule } from 'stuff-lib';\nimport { MaterialModule } from '@angular\/material';\nimport { HttpModule } from \"@angular\/http\";\nimport { FormsModule } from \"@angular\/forms\";\n\n@NgModule({\n  declarations: [Basic],\n  bootstrap: [Basic],\n  imports: [\n    BrowserModule, \n    MaterialModule, \n    HttpModule, \n    FormsModule, \n    LibStarterModule.forRoot()\n  ],\n})\nexport class AppModule {\n}\n<\/code><\/pre>\n<p>After building it with Closure, you will see that this isn't affecting the bundle size much, because the used package is just a minimal demo package. You can also assure yourself that the whole application works. To make sure the package is correctly used by Closure, you can inject its <code>DemoService<\/code> into the Root Component and use it:<\/p>\n<pre><code>import {Component, Injectable} from '@angular\/core';\nimport { DemoService} from 'stuff-lib';\n\n@Component({\n  selector: 'basic',\n  templateUrl: '.\/basic.ng.html',\n})\nexport class Basic {\n  ctxProp: string;\n  constructor(private demoService: DemoService) {\n    this.ctxProp = <code>Hello World<\/code>;\n\n    this.demoService.info = 'Hello World';\n    console.log('demoService', this.demoService.doStuff());\n  }\n}\n<\/code><\/pre>\n<p>After this, rebuild the application again and run it using <code>npm run serve<\/code>. This opens a demo web server on port 8080. Navigate to it and see that the specified message is written to the javascript console.<\/p>\n<h2>Using Closure with a CommonJS\/NodeJS Package<\/h2>\n<p>I've also tried to use a CommonJS\/NodeJS Package with the Closure Compiler. For this, I've installed the package <code>base64-js<\/code> (<code>npm install base64-js --save<\/code>) which I also use for my <code>angular-oauth2-oidc<\/code> project. The easiest way to make Closure aware of such a library seems to be directly pointing to it as you would point to your own files. Alex' sample is this doing for <code>RxJs<\/code> too. For this, add the following line to your <code>closure.config<\/code>:<\/p>\n<pre><code>--js node_modules\/base64-js\/**.js\n<\/code><\/pre>\n<p>After reading this, Closure assumes that the folder mentioned contains a CommonJS Module with the name <code>base64-js<\/code>. The file <code>index.js<\/code> is assumed to be the entry point you can import by referencing the module itself using <code>require('base64-js')<\/code> or <code>import ... from 'base64-js'<\/code>. If there was any other file, it could be referenced using the path <code>base64-js\/other-file<\/code>. To use another entry point, one can also point to the <code>package.json<\/code> using the <code>--js<\/code> flag. In this case, the entry point mentioned within this file in the property <code>main<\/code> is used.<\/p>\n<p>After this, just import the needed parts of the library and use them:<\/p>\n<pre><code>import { fromByteArray } from 'base64-js';\n\n[...]\n\nthis.ctxProp = fromByteArray(this.ctxProp);\n<\/code><\/pre>\n<h2>Dirty Hack: Patching a CommonJS\/NodeJS Package to make it work with Closure<\/h2>\n<p>I've also tried to use the package <code>sha256<\/code> as I'm needing it for my <code>angular-oauth2-oidc<\/code> project. Unfortunately, it doesn't work with Closure. The reason can be found within its <code>package.json<\/code>(<code>node_modules\/sha256\/package.json<\/code>):<\/p>\n<pre><code>\"browser\": \".\/lib\/sha256.js\",\n\"main\": \"index.js\",\n<\/code><\/pre>\n<p>It contains two fields that specify an entry point. By convention, <code>browser<\/code> should be used for bundles that are executed within a browser and <code>main<\/code> should be used when compiling for NodeJS. Unfortunately, at the time of writing this, Closure only evaluates the <code>main<\/code> field. There are <a href=\"https:\/\/github.com\/google\/closure-compiler\/issues\/2433\">some discussions<\/a> about supporting further such properties too. The solution I've found was to manually patch this by setting <code>main<\/code> to <code>.\/lib\/sha256.js<\/code>:<\/p>\n<pre><code>\"browser\": \".\/lib\/sha256.js\",\n\"main\": \".\/lib\/sha256.js\",\n<\/code><\/pre>\n<p>Of course, this is far away from a perfect solution and one has to make sure that the patched files are not overridden by npm. For this, one could copy it to another folder outside of <code>node_modules<\/code>.<\/p>\n<p>After this, just add the following lines to the file <code>closure.conf<\/code>:<\/p>\n<pre><code>--js node_modules\/sha256\/**.js\n--js node_modules\/convert-string\/**.js\n--js node_modules\/convert-hex\/**.js\n\n--js node_modules\/sha256\/package.json\n--js node_modules\/convert-string\/package.json\n--js node_modules\/convert-hex\/package.json\n<\/code><\/pre>\n<p>These lines add the files of sha256 as well as the files of two other packages it depends on. It addition to this, they add the <code>package.json<\/code> of those files so that Closure knows about them and uses them to infer the entry points.<\/p>\n<p>Now you can import the package which is just a function and use it:<\/p>\n<pre><code>const sha256 = require('sha256');\n[...]\nthis.ctxProp = fromByteArray(sha256(this.ctxProp));\n<\/code><\/pre>\n<h2>Repository for Packages compatible with Closure<\/h2>\n<p>As the last section showed, not every package can be used with Closure seamlessly. To help us with this, Alex Eagle from the Angular Team created the repository <a href=\"https:\/\/github.com\/alexeagle\/angular-closure-compatibility\">angular-closure-compatibility<\/a>. The goal is to show which packages are supported as well as to provide examples that show how to use them in an Closure environment.<\/p>\n<h2>Conclusion<\/h2>\n<p>The Closure Compiler is a very powerful expert tool that can reduce bundle sizes dramatically. The Angular Package Format makes sure that (own) Angular Modules work together with it. As Closure assumes that the code is written in a specific way it can be challenging to use it together with other packages.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Shrinking Angular Bundles With Closure<\/p>\n","protected":false},"author":9,"featured_media":2997,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2439","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-unkategorisiert"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Shrinking Angular Bundles With Closure - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Shrinking Angular Bundles With Closure - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Shrinking Angular Bundles With Closure\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2017-06-03T09:19:32+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"853\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Manfred Steyer, GDE\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@daniel\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer, GDE\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\"},\"author\":{\"name\":\"Manfred Steyer, GDE\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\"},\"headline\":\"Shrinking Angular Bundles With Closure\",\"datePublished\":\"2017-06-03T09:19:32+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\"},\"wordCount\":2072,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"articleSection\":[\"Unkategorisiert\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\",\"name\":\"Shrinking Angular Bundles With Closure - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"datePublished\":\"2017-06-03T09:19:32+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg\",\"width\":1280,\"height\":853},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Shrinking Angular Bundles With Closure\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951\",\"name\":\"Manfred Steyer, GDE\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer, GDE\"},\"sameAs\":[\"https:\/\/x.com\/daniel\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Shrinking Angular Bundles With Closure - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/","og_locale":"en_US","og_type":"article","og_title":"Shrinking Angular Bundles With Closure - ANGULARarchitects","og_description":"Shrinking Angular Bundles With Closure","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/","og_site_name":"ANGULARarchitects","article_published_time":"2017-06-03T09:19:32+00:00","og_image":[{"width":1280,"height":853,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","type":"image\/jpeg"}],"author":"Manfred Steyer, GDE","twitter_card":"summary_large_image","twitter_creator":"@daniel","twitter_misc":{"Written by":"Manfred Steyer, GDE","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/"},"author":{"name":"Manfred Steyer, GDE","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951"},"headline":"Shrinking Angular Bundles With Closure","datePublished":"2017-06-03T09:19:32+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/"},"wordCount":2072,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","articleSection":["Unkategorisiert"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/","name":"Shrinking Angular Bundles With Closure - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","datePublished":"2017-06-03T09:19:32+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2019\/04\/blog-2355684-1280.jpg","width":1280,"height":853},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/shrinking-angular-bundles-with-closure\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Shrinking Angular Bundles With Closure"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/15628efa7af4475ffaaeeb26c5112951","name":"Manfred Steyer, GDE","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/a0b59539674d8b71ea1c1f4764b11244b5f499203f1d11b40f37d8f3f90be033?s=96&d=mm&r=g","caption":"Manfred Steyer, GDE"},"sameAs":["https:\/\/x.com\/daniel"]}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2439","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/9"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=2439"}],"version-history":[{"count":0,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/2439\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/2997"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=2439"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=2439"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=2439"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}