-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
Use Closure Compiler with offline template compiler #8550
Comments
The part I think I most need your guidance on is where/how this fits into the larger angular build process. |
We do distribute Angular bundles, as ES5 with UMD module loader built-in. On Mon, May 9, 2016 at 11:26 AM Evan Martin notifications@github.com
|
FYI @jeffbcross maybe this can make your PWA demo for I/O |
Yeah that would be awesome if we could have this working by the end of this week. |
Some notes from the hacks I've had to make so far, in the hopes that others can reproduce: Closure Compiler doesn't handle all ES6 module syntax, and discourages using it. So our strategy so far has been to use Need to build closure compiler from HEAD, see https://github.com/google/closure-compiler/blob/master/README.md#building-it-yourself To build angular into closure ES6, I have local edits in the To build rxjs into closure ES6, I have local edits in https://github.com/alexeagle/RxJS/tree/closure - then run We need a modification to tsickle to workaround ReactiveX/rxjs#1703 - see https://github.com/angular/tsickle/tree/closure Now I make an example app. Snapshot at https://github.com/alexeagle/closure-compiler-angular-bundling So you can just |
So I would need to have a Java JRE installed to bundle a hello world Angular2 app? |
Closure Compiler is just one option for the On Fri, May 13, 2016 at 9:44 AM dpsthree notifications@github.com wrote:
|
I wrote some notes on the hacks required to make it work. https://docs.google.com/document/d/17m1GwzXraKgbCkCmH1JnY9IZzPy4cqlpCFVhvlZnOys/edit |
We reproduced @alexeagle's work and with a more up to date version of Angular. The entire build process is automatic, so you can follow everything from source to end result. You can see it in action at https://github.com/lucidsoftware/closure-typescript-example. Instructions for running the example can be found in the README for the example repo. There is a Docker container for the project, so it should be possible for pretty much anyone to try it out. The above example utilizes Angular 2, clutz, and tsickle. Closure JS is used in Angular 2 TS, which is compiled to Closure compatible JS. The changes we made to the dependencies to get this to work are largely the same as Alex's. There are a few changes we added and a few changes that are no longer necessary. Regardless, the basic idea is the same. We use a modified ngc or tsickle on Angular 2 and its dependencies to produce Closure compatible versions of those dependencies. Closure CompilerBuilding from head is no longer necessary. Just use one of the versions from June or July. Angularhttps://github.com/lucidsoftware/angular/tree/closure-bundle The bulk of the work in the Angular 2 source is modifying ngc to produce Closure compilable js by adding a few tsickle steps to tsc-wrapped. There are a few hoops that we jump through in order to use the modified source as input to the TypeScript compiler in one of the tsickle passes. Otherwise, this is pretty straight forward. We also change certain Angular modules to be compiled to Closure compatible JS by putting an
Then when you run A test utillity file that uses selenium-webdriver is modified because selenium-webdriver is missing and we didn't want to make it closure compatible. RxJShttps://github.com/lucidsoftware/rxjs/tree/closure-hack The build process of RxJS is modified to build with the Makefile from our project. About half the build process is in JS and the other half in Make. It's pretty janky. The only other changes besides the build target are a few small things to make RxJS compile with TypeScript and Closure. symbol-observable (RxJS dependency)https://github.com/lucidsoftware/symbol-observable/tree/closure RxJS now depends on a some code that used to be part of the RxJS source and is now its own module. We modify that module (which is JS) to be compatible with the Closure compiler. There are only two files, so I did this manually. tsicklehttps://github.com/lucidsoftware/tsickle/tree/ignore-type-comments We modify Tsickle in two ways:
|
Whilst it perhaps cannot match the final output size of the Closure Compiler, webpack 2 (using typescript loader and standard optimize plugins) gets you all of the these steps. I would argue that the benefit of devs already using (dare I say requiring) a tool like webpack in their stack, and therefore not having to add more (hacked) dependencies, is more important than shaving off extra bytes in the final payload. Would love to hear your thoughts! |
@jjudd thank you so much for posting your repo and these notes, this is really helpful. Do you have a minified app size you can share? Perhaps using Brotli compression so we have an apples-to-apples comparison with the hello world closure-ng2 example (which was 26.2k) @JamesHenry we agree that webpack 2 is the way forward for most developers. Closure compiler is an expert tool and I doubt we can wrap it up in a package that 'just works' for developers who aren't already familiar with debugging the obscure ways its ADVANCED_OPTIMIZATIONS can bust your app. |
@alexeagle We're currently working on getting this to work with ADVANCED_OPTIMIZATIONS. When we get that working I'll go ahead and post some benchmarks. |
Converted the tsickle issue 1 above into a bug report there. |
closure compiler is now available on npm in pure javascript : https://www.npmjs.com/package/google-closure-compiler-js |
Wanted to provide an update: We have the example repo and the beta Lucidchart editor working with advanced optimizations. We ran a short test using our version with simple optimizations, and our version using advanced optimizations is going out today or tomorrow. The version of Angular used in the example repo has been upgraded to RC5 (HEAD as of Tuesday Aug 16). The example repo and its accompanying Docker container have been updated in case anyone wants to try it out. Here's a link to the example repo https://github.com/lucidsoftware/closure-typescript-example Bundle sizesThe final bundle size for the example project using advanced compilation are listed below. Note: the example repo includes code going from js -> ts using clutz and then ts -> js using tsickle. It is a bit bigger than just a simple hello world app.
Notes on Getting This to WorkHere are the notes on what it took to get advanced optimizations working.In case I forgot something, I'm going to provide the links to all our forks, which have all of our commits. Example repoExterns files for Zone, Reflect, and Jasmine were included in the build process, so those function calls were not renamed. To work around a Closure Compiler bug where static methods get lost, we have a really hacky sed command that we run on the built Angular js files, replacing all lines which include a static keyword with the line preceded by /** @nocollapse */. Here's a link to the Closure bug: google/closure-compiler#1776 AngularThe biggest change we made to Angular is making it use the Tsickle annotated output during compilation. We thought we did this last time, but we had a bug where the annotated output was being thrown away :D There were a few places that Angular refers to functions as strings. These functions were referred to in other places using dot notation. Accordingly, some references were renamed and others were not. We change Angular to use dot notation everywhere for those functions, so that they are always renamed. We also updated Angular/Tsickle to not annotate .d.ts files. We made Angular play nicely with ES6, so it works in uncompiled mode. We change some places where .apply was used on a constructor to use the new keyword. TsickleWe fixed a bug in tsickle where the CLI did not always include all the source files during annotation. The list of filenames from the TypeScript Program should have been used, instead we were using a different list of filenames. Links to all our forks of projects to make this workAngular: https://github.com/lucidsoftware/angular/tree/closure-bundle |
So we have a couple examples of this working, plus we use JSCompiler internally at Google so we have many apps doing this (though using the Bazel build system and some not-yet-released Bazel rules). @robwormald @mprobst should we try to package this up in a more easily reproducible way for external users? I don't think we can claim success and close this issue yet. |
The JS closure compiler looks really cool. However, I have seen issues where the process runs out of memory when targeting medium sized AOT projects via Rollup. Have you encountered this in your testing? The Java version does not seem to suffer from this problem though. |
I've heard that too. We have had to call node with On Tue, Sep 20, 2016 at 11:27 AM Torgeir Helgevold notifications@github.com
|
I tried |
oh, that seems bad :) On Tue, Sep 20, 2016 at 11:52 AM Torgeir Helgevold notifications@github.com
|
Yup I have an active issue there already |
@nosachamos Wild guess: something in the toolchain (probably tsickle or Closure?) did not anticipate a parameter named
|
@kylecordes Ops, I found my issue and deleted the comment I guess as you were typing. For the record, my issue was that an argument named "this" was being removed during transpilation. It turns out, closure was invoking that function using My issue was within ngrx effects, not rxjs. Ngrx/effects accesses the effects through their names, using the brackets notation, which is broken by the closure compiler when it renames all the things. To prevent the issue, in the constructor of your classes containing your effects, assign their names to "this" using brackets notation:
So I'm almost up and running on a real world app using the ngrx stack. I'm writing what I believe to be the last manual extern required for my case. If this all runs well, I'll create a branch on the sample app here with the ngrx stack as a demo. I'm using my own fork of ngrx/core and ngrx/store while my PRs for expanding wildcard exports are not merged, but other than that, I think this will go well. I'll keep you guys posted. |
Can you file a bug against tsickle with more information on the Is it possible to fix ngrx to not use brackets? |
@nosachamos based on your deleted comment, and my maybe-should-be deleted comment, is there any issue to report? (per @evmar ) |
@kylecordes Maybe, @evmar I believe so. This bracket access is quite central to their operation but I suspect annotating the underlying dictionary with closure's @dict annotation would suffice to fix the issue. I will give this a try and submit a PR to them if that works. Else, I'll submit a PR to their README with a note on the bracket workaround when using ngrx/effects with closure. |
Note that any Closure type annotation added to TS code is stripped by Tsickle, so adding an @dict there won't help. |
@nosachamos Won't the same issue happen with |
@evmar I'm transpiling with @awerlang no, the @dict is a jsdoc style annotation, not an actual annotation like @input. These closure annotations go on comments. |
@nosachamos our test case is this input https://github.com/angular/tsickle/blob/master/test_files/jsdoc/jsdoc.ts producing this output |
@evmar oh, I'm compiling with ngc and using
and
So I get just ES6, no goog.modules.. so everything seems to be working as intended, and I get the closure annotations. This is for RxJs. For packages that interact with angular (components, modules, etc), then |
@nosachamos as I understand, the tsickle step can't do it's thing, and won't do its thing when run via |
@kylecordes yeah, I get the same exact output you get in the demo repo you posted above compiling with tsc-wrapped and the rxjs-tsickle tsconfig. |
could we move bugs to another issue? it doesn't scale to address individual problems on the tracking issue. Thanks! |
@alexeagle absolutely. I posted here initially because I thought this was relevant (a problem in how rxjs was declaring arguments named "this" that were being lost). There's nothing to follow up, but if something comes up we'll do in a separate issue. |
@nosachamos Indeed... I just found that indeed, tsickle-via-ngc or -cli-wrapped can handle @alexeagle Ah, oops. At 90+ comments, I didn't realize it was a tracking issue. Will make new issues if they come up. |
Took another look at the generated bundle. Noticed that there is definitely room for further code reductions here. It's not always easy for Closure to identify unnecessary code since the existing code structure sometimes makes it seem like the code is needed. Based on the POC app in @alexeagle 's repo I picked a few unnecessarily included elements and made a few temporary tweaks to the source to force them out of the bundle. Seems like there are a few repeating patterns here: A service class may not be needed by the application, but if the service is added to a provider array along the way, it may cause the class to make it into the bundle. Examples of this are The sample app in Alex's repo does not do any QueryList querying, but a lot of the Query related code is still in the bundle.
The conditions in this method are resolved at runtime , so Closure has to include all the code to satisfy all the conditions in the switch. I removed this particular clause and the code was excluded from the bundle. These are just two simple examples, but if you continue down this path, it will eventfully add up to noticeable reductions in size. I didn't go very far with this sample, but I probably removed roughly 1k by addressing 4-5 cases like this. I also did a second experiment where I removed a bunch of code manually (without using Closure). I got my app down to 16.6k and I believe there is still room for removing more. Here is the sample: http://www.syntaxsuccess.com/viewarticle/minimal-angular-application . This particular experiment is a major hack, but at least it proves that you can stand up an Angular app with very little code. To help with some of these challenges I was thinking it would perhaps make sense to extend AoT to generate more of the framework code - not just the view code. Just thinking out loud here, but couldn't the ngc compiler in theory generate code for stuff like Thoughts? |
Based on what I learned from manually removing code in the bundles, I have applied similar modifications to the bundles in the closure fork. I put the tweaks in a branch in case you are interested in seeing the results: https://github.com/thelgevold/closure-compiler-angular-bundling-old/tree/tweaking-src Here is a diff of the original bundles vs the tweaked bundles: thelgevold/closure-compiler-angular-bundling-old@d1b3954 If you want to try it, just replace the default bundles with the modified ones. I am assuming Angular 4.0.0 for this experiment. Here are the numbers with the new bundles: The new size is now ~15k gzipped, and slightly smaller with brotli.
Obviously it's hard to replicate this without restructuring the Angular source, but it's at least interesting to see how small a basic app can get. Not sure how useful this is, but it's at least a fun experiment :-) In summary: |
As I understand from @thelgevold 's work, there is considerable output size reduction, with relatively minor-to-moderate adjustment to Angular:
I'm interested in what someone on the core team thinks of this, whether it really is minor-to-moderate and whether there is interest in going down this path. |
Today we got a release of rxjs that works with closure compiler, and I've updated the example repo The externs are cleaned up as well - Angular and Zone.js both distribute the needed externs in their respective packages. |
Now that all of this is out of the way, at this point do you have an idea of how it will end up integrating with the rest of the existing tooling? e.g. might there eventually be an option added to @ngtools/webpack to automatically run the AOT output through Closure (and/or some documented configuration steps for webpack-closure-compiler)? Or is this something that will necessarily have to supersede webpack? |
Is possible to implement |
hi I installed your project but when do "make run" get Error : |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Angular's new offline compiler was demo'ed at ng-conf day 2 keynote by @robwormald. It generates tree-shakable ES6 code. Four steps are then required:
This produced a 49k JS file, but requires a lot of configuration.
Google's Closure Compiler (https://developers.google.com/closure/compiler/) produces very small JS. It does all four steps required above, so the configuration should be a lot simpler. We also suspect we can get a smaller binary size for ng2-hello-world, around 36k.
Wiring this up requires:
BrowserNodeGlobal
type)The text was updated successfully, but these errors were encountered: