Git Product home page Git Product logo

Comments (190)

chaser92 avatar chaser92 commented on March 29, 2024 349

I think that this isn't the best idea - TypeScript should better do what it's best at, one tool should serve one purpose. There are a lot of great minifiers out there.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024 302

@chaser92, this request is to minify TypeScript code, not JavaScript code. A minifier that uses the information (primarily information on access modifiers) available to the TypeScript compiler will be much more efficient than any JavaScript minifier out there.

I am personally very eager to see this implemented.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024 130

The following TypeScript code

class Greeter {

    private greeting: string;

    constructor (message: string) {
        this.greeting = message;
    }

    public greet() {
        console.log(this.getMessage());
    }

    private getMessage() {
        return "Hello, " + this.greeting;
    }
}

Can be compiled into JavaScript that when run through an external minifier results in the following:

var Greeter = (

    function () {

        function a(b) {
            this.greeting = b
        }

        a.prototype.greet = function () {
            console.log(this.getMessage())
        };

        a.prototype.getMessage = function () {
            return "Hello, " + this.greeting
        };

        return a
    }
)();

Problems:

  • The private field greeting has not been minified.
  • The private method 'getMessage` has not been minified.
  • The type name "Greeter" has been mangled into "a". This breaks code such as (/function (.{1,})\(/).exec((instance).constructor.toString()) for obtaining the type name at runtime.
  • The constructor parameter "message" has been mangled into "b". This breaks code that attempt to perform dependency injection by parsing constructor arguments.

In summary, even in just this simple snippet of code TypeScript can improve on an external minifier, both by mangling names that should have been minified as well as optionally leaving names untouched.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024 29

If I use the Closure Compiler, it goes from code like this:

var Greeter = (function () {
    function Greeter(message) {
        this.greeting = message;
    }
    Greeter.prototype.greet = function () {
        console.log(this.getMessage());
    };

    Greeter.prototype.getMessage = function () {
        return "Hello, " + this.greeting;
    };
    return Greeter;
})();


var x = new Greeter();
x.greet();
console.log(x.getMessage());

to this:

var b = new (function() {
  function a(a) {
    this.c = a;
  }
  a.prototype.b = function() {
    console.log(this.a());
  };
  a.prototype.a = function() {
    return "Hello, " + this.c;
  };
  return a;
}());
b.b();
console.log(b.a());

Here, the greeting field and getMessage names have been correctly minified. The "over-minification" of other variables leads to my next discussion point on this.

Some people think class names, property names, parameter names, local names, etc are meaningful runtime metadata, other people think they're not. Some other people think only some of their property names, parameter names, local names, and so on are things that should be minified. Among those people, there's disagreement over how those names should be specified (globally? locally? in the code? in a config file? on a commandline?). Nearly everyone believes that their set of rules is the only one that makes sense.

The only path forward that could beat existing minifiers also involves a ton of configuration for what the allowed set of minifications even is; implementing and testing all these rules would be very expensive. It's a lot of investment for what is probably a few percent improvement (especially after gzipping) over the state of the art here. That's why we want to see compelling examples for where only the TypeScript compiler could minify and do a meaningfully better job than any existing tool.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024 25

To summarise the discussion so far:

  • The JavaScript generated by TypeScript is not optimal in terms of providing that output as input to a simple minifier. This is largely due to private methods going on the prototype. A developer wanting to write code that is optimal for a minifier would have written those private methods as simple functions within the class closure.
  • This problem can be solved by running the output through an advanced minifier (for example the closure compiler with the "advanced" option).
  • Unfortunately the closure compiler requires all symbols to be within sight of the minification run. If they are not then those symbols must either be exported (added using bracket notation) or an externs file must be created, declaring the symbols that should be preserved.
  • User preferences for minification can be diverse.
  • The TypeScript compiler has a superior information set with which to perform minification than a JavaScript minifier; this information set includes having access to the following modifiers "class", "private", "public", "constructor" and the API of external libraries through their declarations files.
  • The biggest advantage that a TypeScript compiler has over ordinary minifiers is being able to safely perform minification on any subset of the code-base. By "safely" I mean that, firstly, exported classes and their public API can be preserved, and, secondly, calls made from within a class into external libraries can also be preserved with recourse to the information in their respective declarations files.

In the light of the above, I have three basic proposals listed in order of preference:

A. A fully functional minifier provided by TypeScript.
We provide two compiler options

  • --minify-simple - Preserves class names, constructor signature...
  • --minify-advanced - Performs a more aggressive minification.

In both cases, private fields and methods would always be minified.

B. TypeScript generates externs files for providing to the Closure Compiler if specified.

C. TypeScript only minifies private fields and methods if specified.

It is conceivable that with option A there will be many people who would like a halfway solution between "simple" and "advanced" minification, e.g. preserve class names but minify constructor signature. In this instance, it may be possible for those concerned to run the output generated by TypeScript through a third-party minifier that provides more specific options.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024 18

Motivating examples of things that TypeScript could minify but an external minifier could not would be useful. Things like closure and uglify do a really good job already; we'd have to have evidence we could make real improvements over them to justify spending time on it.

from typescript.

 avatar commented on March 29, 2024 17

Don't compilers generally have a switch to optimize for file size? Why not this one?

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024 12

@RyanCavanaugh,

Regarding the under-minification problem, it looks like the code in your post was minified using the "advanced" option of the closure "compiler". And yes, that appears to solve the problem in this simple case. However, consider this slightly more involved example:

class Greeter {

    public greet() {
        console.log('greet');
    }
}

function createInstance<TType>(name:string): TType {

    return new window[name]();
}

var x = createInstance<Greeter>('Greeter');
x.greet();

Basically, we have introduced a class factory function. The closure compiler, with the advanced option, minifies this to

(function() {
  function a() {
  }
  a.prototype.a = function() {
    console.log("greet");
  };
  return a;
})();
(new window.Greeter).a();

Clearly this is going to fail at runtime. The solution is to export the "Greeter" symbol:

window["Greeter"] = Greeter;

Seems simple enough. But in large projects with hundreds of classes, this is not trivial. TypeScript understands this code better, because it knows that function Greeter is a class name.

More importantly, the problem I have with the closure compiler is that it requires the entire code-base to be minified in one go. This is not feasible in large projects, where there is separation between library code and client code. In this situation it is necessary to declare externs, which ultimately results in maintaining one's public API in duplicate.

With regard to the second issue that was raised, namely the problem of configuration, that, I guess, is an implementation detail: if there is demand for configuration then that would have to be dealt with. But it seems strange to decline an entire issue on that basis.

A possible halfway solution would be for TypeScript to provide an option to generate closure-style extern files or to export symbols (class names).

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024 8

Meta: Is this the canonical issue tracking the proposal to create a path to an optimizing backend (be it built-in or via Closure annotations)? It appears to be at a glance, but if I'm missing a more appropriate spot, I'd appreciate a pointer to it.

I'd just like to throw in my 2¢ in support of an optimizing backend. My company's using Typescript very heavily, and while using Closure's basic optimization mode on the output helps to a certain extent, it's quite obvious that we can do a lot better. Code size may not matter to everyone, but it should -- it's not just about over-the-wire size, which is (mostly) mitigated by gzip, but about memory use, parse time, and runtime performance (yes, you can still achieve significant improvement through static optimization). There's a reason that Google invested heavily in optimization for both the Closure and GWT stacks (full disclosure -- I worked a lot on GWT and to a limited extent on Closure when I was there).

Just to be clear, this is what I mean when I suggest that we can do a lot better than just dropping whitespace and obfuscating local identifiers:

function rAb(a,b){if(!b.ib.a){Sae(a.a.a.a.a.d,(Klf(),new Nwf(b)));bOe(a.a.a.a.a.i,a.a.a.a.a.i.c)}}
function Cqb(a,b){a.f=b;if(!b.yb){a.a.we(new Aqb(a.b,a.f,null));return}wqb(a.d,b.Fb,new Iqb(a))}
function Bqb(a,b){var c;a.b=b;c=b.g;if(c.fh()){Cqb(a,b);return}vae(a.c,c.Br(0).Fb,false,new bbe(new Gqb(a)))}
function Pfd(a,b,c){var d;d=new Ufd(a);Bgd(a.a,d);Agd(a.a,b,c);dgd(a.b,new Wfd(a));Jpb(a.b,new Yfd(a));Qfd(a)}
function Ppd(a){var b;b=new qid(Dte(AFd(a.a)),bue(AFd(a.a)),aue(AFd(a.a)),Opd(a),knc(XEd(a.a)));return b}
function $pd(a){var b;b=new kmd(NXf(QFd(a.a)),(new rnd,KFd(a.a),new ndf),new hid(pKc($Ed(a.a))));return b}
function Rnd(a){var b;Sgf(a.s);if(a.o.S){return null}b=a.U.vg();return !!kfi(b).length&&!u_(b)?b:null}
function Pnd(a){Sgf(a.s);if(a.o.S){return nt(),mt}if(a.N.e.f.a){return nt(),mt}else{Sgf(a.M.e.f.a);return nt(),kt}}
function Gld(a){if(!a.j||!ZM(a.d.b,(Jw(),Qv))){return scf((Yci(),Yci(),Wci))}return icf(a.n,new Jld)}
function Mkd(a){a.a.wk((RJf(),KJf));wbb(a.a,eui);a.a.Oe(true);Fbb(a.a,new Ukd(a),BRf?BRf:(BRf=new wQf))}
function Jhd(a,b){var c,d;d=Bae(a.c,b);c=Lld(Ypd(a.a.a),b,d);return Nhd(new Ohd(a),(Ngf(c.d),new Ild(c,true)))}

The above is a random chunk of GWT output from a Google app. The details don't matter -- the point being that aggressive optimization can dramatically reduce output size, make a non-trivial difference in parse time and runtime performance, and even amplify gzip compression by allocating identifiers in such a way as to reduce input entropy.

As has been pointed out earlier on this thread, there are two obvious routes we can take:

  • Build an optimizing backend into tsc.
  • Add an option for generating Closure jsdoc annotations.

I don't have a strong preference -- as long as it's possible to get good output, I don't care much how we get there. The output example above came from the GWT compiler, but Closure achieves similar results. Michael Bolin (who did a lot of the work on Closure at Google) created a proof-of-concept (http://bolinfest.com/typescript/) a couple of years ago, but didn't take it much further than that. It's not an entirely trivial exercise, because of the impedance mismatch between Typescript and Closure's inheritance mechanism in particular, but it doesn't seem like brain surgery either.

The hardest design problem, as I see it, is dealing with exposed vs. optimizable symbols. Closure has annotations for dealing with "exporting" symbols, and Typescript would need something similar to make the optimizer useful. There are also important edge cases like dealing with externally defined objects (both importing third-party libraries, and dealing with the output of APIs like JSON.parse). The compiler must know about these things if it is to avoid breaking the output with an aggressive optimizer.

I think it would be fairly easy to rally a few people to work on an optimizing backend for Typescript, but only if the team is bought into the idea. Trying to bolt such a thing onto the existing compiler, without the ability to tweak the compiler and language a bit, is probably a fool's errand. So I'd greatly appreciate any indication from the team as to their disposition on the subject.

from typescript.

eggers avatar eggers commented on March 29, 2024 5

I would love a typescript aware minifier. Also, because I've had some issues with ng-min not working very well on tsc generated javascript. (I haven't tried ng-annotate yet)

from typescript.

kitsonk avatar kitsonk commented on March 29, 2024 4

Because TypeScript emits idiomatic, valid JavaScript, I suspect it isn't a priority because there are plenty of other tooling out there that does better than likely what the TypeScript can hope to achieve in the short term. Honestly folks, if you depend on TypeScript for you minification, you are likely not going to do an effective job of it. There is the Closure Compiler and UglifyJS. You can have those today, actually you could have had them before TypeScript.

from typescript.

antoinerousseau avatar antoinerousseau commented on March 29, 2024 3

If option A is fully efficient, I don't see why you would need B or C!

I would love to have option A's --minify-advanced!

from typescript.

mirhagk avatar mirhagk commented on March 29, 2024 3

Compilers don't usually have a goal of near 1:1 mapping with source.

The point is that generating the resulting code is completely different with optimizing for file size (and other optimizations) than what it currently produces. I'd suggest a separate tool is created for an optimizing compiler (one that optimizes for either file size or performance), that makes use of the API tsc offers. I was planning on experimenting with the tsc compiler API so I may do an experiment in producing minified output.

from typescript.

jimbarrett33 avatar jimbarrett33 commented on March 29, 2024 3

I think this is a must have for TypeScript. It's a great language and gaining a lot of support, especially with the Angular 2.0 stuff. Now that I have developed a lot of TypeScript code and almost ready to deploy I now wish I hadn't used such long and descriptive naming (which I always do). Unfortunately, I thought they would minify OK. I tried using Closure but it broke more than it helped.

Coming from C# background I think annotations are the way to go. But they should be "opt out" instead of "opt in" for class properties and methods (both private and public), therefore they are minified by default.

Is there a way to elevate this so that someone on the TS team can at least tell us if it is being considered in the near future?

Until then I'm going to try to roll something on my own (which will probably leave me bruised).

from typescript.

hcapp01 avatar hcapp01 commented on March 29, 2024 3

I do not understand why those things in ts 1.7 roadmap can be more important than minify. I had no choice but shipped my code with full function names. I am feeling myself as unprofessional as selling apples with apple tree attached, selling house without scaffold staging removed, taking my girlfriend to motel with her mom in the trip.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024 2

I really like the "random chunk of GWT output from a Google app". In addition to the benefits mentioned above, another objective is protection of intellectual property through obfuscation. If anybody has tried stepping through the code in Google Maps then they will know the protection that aggressive minification can provide in this regard.

The annotation (for excluding minificaton) is not only relevant for externally defined objects, but also relevant when properties of JavaScript objects are bound to elements in the HTML.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024 2

The point about private methods is, given the following TypeScript

class Foo {

    public myPublicMethod(){
        this.myPrivateMethod();
    }

    private myPrivateMethod(){
    }
}

We get JavaScript output:

var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.myPublicMethod = function () {
        this.myPrivateMethod();
    };
    Foo.prototype.myPrivateMethod = function () {
    };
    return Foo;
})();

Running the JavaScript output through uglify produces:

var Foo=function(){function t(){}
return t.prototype.myPublicMethod=function(){
this.myPrivateMethod()},
t.prototype.myPrivateMethod=function(){},t}();

This minification is inefficient, because the private method should have been reduced to a single character, say a instead of myPrivateMethod. Doing so would improve minification by 4% for this simple example. Unfortunately, uglify cannot do anything about it (without further annotation) because the TypeScript compiler emits both the private and public methods on the prototype, so that there is no distinguishing between the two.

Those writing JavaScript libraries would typically write their code in such a way as to obtain better minification, for example:

    function Foo() {
    }
    Foo.prototype.myPublicMethod = function () {
        myPrivateMethod.call(this);
    };

    function myPrivateMethod() {
    };

It is therefore likely that many library developers are put off from using TypeScript because of these inefficiencies.

So, while using uglify is not "problematic" as such, it will not be as efficient as minification generated by a system that understands private methods, that is, minification generated directly by TypeScript.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024 2

We haven't ruled it out. If someone has waited three years without looking for an alternative, they apparently don't need minification all that badly and I'm inclined to not proceed with it.

This has been sitting in "Needs Proposal" because, as I've outlined here many times, we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers, in a way that is general-purpose enough to work for most people.

Our current belief is that this might be possible, but is almost certainly a huge amount of work that we could spend in more productive ways. I don't think it would be a great trade-off for us to invest engineer-months in a minifier that is 3% better than what's sitting on NPM today in exchange for not having a smarter type system or not having better editor support or not having the compiler be 15% faster.

If someone put up a fork with a prototype minification algorithm that produced substantial minification gains over the competition, we'd find that very convincing. If no one is bothering, that really is a sign that while everyone might enjoy the pleasant convenience of built-in minification, it's not a area where we can deliver a lot of value.

from typescript.

 avatar commented on March 29, 2024 1

I like your B suggestion (TypeScript generates externs files for providing to the Closure Compiler). Not sure how it works but I like the sound of it when considering the points that everyone else is making.

from typescript.

mirhagk avatar mirhagk commented on March 29, 2024 1

Yes that's true, but then the back-ends are basically completely separate. I'd recommend implementing it as a separate module/project that uses the API, then if it does prove very useful it can be evaluated to be merged as part of the tsc command line.

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024 1

I decided not to say anything about obfuscation for the purpose of, well... "obfuscation", at least partially because I know that tends to raise a lot of hackles in the web world :) A bit more seriously, I'm less concerned about it simply because I've spent so much time reverse-engineering highly-obfuscated Javascript (e.g., http://www.j15r.com/blog/2005/02/09/Mapping_Google) that obfuscation only feels like a speed bump. But hey, a big speed bump can still be useful.

Regarding annotation, right -- I forgot to mention that runtime-provided types are equivalent to external code. The (small, but important) distinction being that you tend to have relatively stable and available IDL for the former, whereas the latter's a bit of the wild west. If you have type information (e.g., via DefinitelyTyped), you're good to go, but if not you have a problem. Personally, I'd be fine with requiring explicit type information for all external libraries as a precondition for aggressive optimization, but I'm not sure that's a majority opinion.

FWIW, Closure basically takes that approach. If you don't have type annotations for external code, Turing only knows what it will do under advanced optimization (actually, I don't even think Turing knows, because it's probably undecidable, and the compiler certainly can't tell you). With GWT it was a bit easier, because you needed explicit (lightweight, optimized away) Java interface wrappers to call into Javascript in the first place. So the compiler only has an aggressive mode, because it can prove that the output won't be broken (as long as your Javascript methods don't "cheat").

Requiring explicit types also works for dealing with parsed JSON (our rather large body of Typescript uses interfaces for all parsed values; otherwise we'd be breaking things left and right). I believe Closure allows for this, but the traditional method was to require string accessor syntax for any properties that weren't defined by the compiler (e.g., value = parsedThing["someField"]). This is a pretty foul hack that I believe goes unchecked by the compiler, though, and I wouldn't be in favor of doing anything that nasty in Typescript. After all, that's one of the great benefits of making interface definitions an order-of-magnitude less verbose than in Closure.

from typescript.

danquirk avatar danquirk commented on March 29, 2024

The configuration is not just an implementation detail. The fact that we can go back and forth all day with different code examples that need differing amounts of minification is proof of the need for either a) very simple minification algorithms b) extremely customizable minification options. You've already identified multiple issues that absolutely demand customization to work at all.

In addition, we're still talking about this nebulous concept of 'the TypeScript compiler understands the code better so it should just do this example correctly' (for some definition of correctly). There's still very little here that is actually stating proposed solutions to classes of problems which you could put a configuration over the top of.

from typescript.

bryanerayner avatar bryanerayner commented on March 29, 2024

TL;DR
Perhaps Data-Annotations could be added to Typescript's syntax to provide developers with an easy way to tell the compiler what should, and should not be overly minified.

I have been writing a large Angular app using Typescript. I've been painfully watching the size of the JS grow and grow, and would personally LOVE to have some advanced compilation support that only Typescript can provide. IMO Typescript can do an incredibly good job at minifying code, better than closure because you're not limited to comments for specifying compiler directives.

For an Angular app, there needs to be a high level of granularity of what functions and properties are minified (a la Closure Compiler's advanced mode) and what properties should be left as-is. They are referenced in the HTML and oftentimes used by Angular to correctly link the DOM to JS. You'd need a solution on an item by item basis in order to accomplish this in a way that would make it easy to use.

For example, much in an Anuglar directive can be truly 'Javascript Only', and I would like advanced minification on it. However, there are some variables that need to be exposed to HTML, outside of the javascript engine.

If we were able to use something similar to a C# data-annotation on class properties, that would be a much better method than Closure compiler's recommendations. I've seen a lot of Javascript written for the Closure compiler that uses array notation to reference unminified properties - This is a pain to write. Any time you reference a property by a string in Javascript, it just feels muddy to me.

I've been using Newtonsoft.Json on a dot Net backend. We directly serialize our business logic models to the client side - Of course, we want to keep some things hidden from JSON serialization. For those without a C# background, a data annotation looks like this:

Imports Newtonsoft.Json

class Klass
{
    [JsonIgnore]
    public string[] SomethingVeryLarge;

    public string SomethingMoreManageable;
}

The [JsonIgnore] data annotation instructs Json.Net to overlook this property when parsing an instance of Klass.

Having something like data-annotations could provide the compiler with a really good system of flags, that could be used for this advanced minification support, or other compiler features. I could see this syntax eventually being usable Typescript programs to further extend the language.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@bryanerayner,

there are some variables that need to be exposed to HTML, outside of the javascript engine.

Yes, that's a relevant point as well. This is also the case when using KnockoutJS, for example:

class ViewModel {

  [minifyIgnore]
  public foo = ko.observable('bar');
}

because foo is referenced in the HTML:

<div data-bind="text:foo" />

from typescript.

WanderWang avatar WanderWang commented on March 29, 2024

I Agree with @RyanCavanaugh
I'm working for a Open Source HTML5 Game Framework called EGRET
Many of our developers thinks that our game.min.js should be smaller and smaller .
Now we compress our game.min.js with Google Closure Compiler SIMPLE_OPTIMIZATION , there are some reason we abandon ADVANCED_OPTIMIZATION

  • It's very hard to use ADVANCED in a complex project with Zero bug . We had to test the whole the test case carefully again . When we turned to SIMPLE mode , it has too many private long-named field not be optimized , C is a useful solution with SIMPLE mode
  • We should support our customer a lib.min.js and there are many public api inside . But ADVANCED_OPTIMIZATION delete almost all the apis because it thinks that the apis not be used . To solve this problem , we should provide a extern-file to Closure Compiler , but we have to many API . B solution with ADVANCED mode is a good idea , because of tsc can generate the extern files very easy ( such as .d.ts )

So, I hope both B and C could be added to tsc .
By the way ,forgot A ,please ^_^

from typescript.

mirhagk avatar mirhagk commented on March 29, 2024

I'm noticing that the problem with using Closure is that there is information about visibility that TypeScript is aware of that Closure can't know.

This is why the private members stick around when using simple with Closure. Using advanced can cause headaches.

TypeScript can make it easier to use advanced with option B. It could also potentially make simple optimizations with closure better if it could communicate to closure which methods/variables are private. It could emit JSDoc comments to decorate fields with @private.

Currently the Google Closure compiler doesn't use these hints for anything useful, but providing that information for it doesn't hurt (since it removes comments) and it gives minifiers more information to optimize with.

from typescript.

mirhagk avatar mirhagk commented on March 29, 2024

I think the issue is that the compiler shouldn't necessarily be responsible for minification. I would much rather see all the information that TypeScript knows be emitted as jsdoc comments. That way JavaScript tools can utilize that information without needing to have a TypeScript parser.

from typescript.

 avatar commented on March 29, 2024

The goal of the TypeScript compiler could be near 1:1 mapping of source unless the /min switch is set, in which case the goal would be to minimize it to the hilt without changing its exports.

from typescript.

 avatar commented on March 29, 2024

@joelgwebber that is a really great appeal.

Just wondering: with variables and modules/classes and their members being necessarily declared, can't the optimizer ONLY rename members which are traceable back to a declaration? An any or an any which is known to implement an interface is still external code and those identifiers are not declared. (And declare declarations don't count.)

But in code with multiple namespaces, we would be forced to export too many items - items which are needed within the application by different namespaces, but not outside of it. So there'd need to be some additional level of accessibility between non-export and export, like internal?

from typescript.

fletchsod-developer avatar fletchsod-developer commented on March 29, 2024

Don't forget there should be available an option in Visual Studio --> Config Transform (transformation) for Web.Config that are tied in to Visual Studio's Configuration Manager. That way we as developers should not be seeing minified javascript during development, especially when there's error or when debugging javascript source code.

I know this is about TypeScript minification thing but when in Visual Studio runtime via F5 key, it compile TypeScript to Javascript & I wouldn't want to see minified Javascript code during developing. Also, the Config Transform are tied in to publisher too. I would rather have minified javascript code when publishing to production sever but not minified to developer server.

Just saying.

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024

@GITGIDDY : I believe that's correct. The compiler can't go renaming symbols whose provenance are not known, and which it can't prove haven't "escaped" into any references or external code. In a language like Typescript, this requires global type inference. I know the compiler does a certain amount of type inference already, but I don't know how broadly scoped it is -- if it's just for locals, that could prove problematic.

@fletchsod-developer: Of course. We'd be nuts to run an optimizing backend with global type inference on every compile. You really only want that before deploying to production.

from typescript.

bryanerayner avatar bryanerayner commented on March 29, 2024

I really would love to see this issue moved forward. It's evident that this is something people want (few users of Typescript would object, I'd challenge anyone to find a die hard objector). The issue is, it needs to be implemented correctly.

@GITGIDDY, I think you're absolutely right about the need to declare modules as internal vs external. But to add a keyword to the language for the sole purpose of minification isn't compelling. Private, protected, public - all of these keywords are very helpful to us for purposes other than minification, though they're incredibly helpful for that purpose.

With the merger of Typescript and AtScript, we finally have a syntax that's endorsed for data annotations. Is there a roadmap for specifying which annotations can be picked up by the compiler, I wonder? I'm sure that's something that's wanted by many people, so I'll assume that's avenue to pursue on a configuration level.

What if we were to steer this thread to a conversation about some standards which could be implanted, should minification be a feature of the compiler?

To start -

  • is it safe to assume anyone desiring a higher level of minification than UglifyJS will be willing to put the work into their codebase, as to not use implicit any, and have every piece of data defined by an interface?
  • Is there a real objection to NOT minifying private and protected members?
  • Can annotations be used to provide directives to the minifier?

Solving these questions would be a good start. I feel that there could be more taken though:

  • If annotations cannot be used on Enums, interfaces, or modules (as I don't see that in the AtScript syntax) how can we provide information about the intended minification level for these features? Can the annotation system be extended to work on things other than classes?

Don't get distracted by the last point there. Let's start with the first three? I feel like there could be some progress on something if a common consensus began to emerge.

from typescript.

 avatar commented on March 29, 2024

Perhaps with Ron Logan's (aka ronlo http://ajaxmin.codeplex.com/team/view) expertise, the development of this feature can elevate? He has done tremendous work in JavaScript minification over the past several years.

from typescript.

Ciantic avatar Ciantic commented on March 29, 2024

I think main selling point for dead code elimination is util libraries like lodash/underscore. TypeScript would become immediately much better for compiling those, than e.g. modularity of lodash which requires programmer to keep the mini-packages updated.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

Is there a way to elevate this so that someone on the TS team can at least tell us if it is being considered in the near future?

Minification has always been one of our most requested features, and one that we have talked about on and off throughout the past few years. It is a substantial amount of work though, assuming you are not just looking for white space removal, and variable name shortening. Moreover, minification has existed in JavaScript tooling for a long time, and there are already multiple minifiers that plug into existing JS workflows. We have been really busy with ES6 and other language/tooling features for TypeScript which made us shy from taking on such a big problem; specially that TypeScript compiles to JavaScript which allows you to leverage any existing JS tools out there.
That said, it is not that we are ignoring the issue; it is still open because we believe that there is problem out there that TypeScript can help solve.

from typescript.

jimbarrett33 avatar jimbarrett33 commented on March 29, 2024

@mhegazy, thanks a lot for the reply. I'm sure you're very busy and I wasn't trying to push! I just wanted to know if anything was on the near term radar. Good luck with the ES6 stuff. TypeScript is great.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

I second @jasonwilliams200OK's suggestion regarding Ron Logan. They have a well-maintained minifier (that I have used in the past) that could possibly be adapted to work for TypeScript. I have found Ron very proactive on the subject.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

We are currently doing some JSDoc infrastructure work including parsing and AST representation, once that is done, generating type-annotated JSDoc comments should be fairly easy and will unblock more minification/optimization scenarios. We would be open to taking a PR for that.

from typescript.

raphaelfeng avatar raphaelfeng commented on March 29, 2024

Would like to see TypeScript compiler to support advanced minification feature!

So far, I have to create the externs file by myself and use the bracket notion to export the symbols.

For now, I am trying to generate the extern files from the .d.ts type file (aforementioned as Option B by @NoelAbrahams) by using typescript compiler APIs, so I don't need to manually change the externs file every time I use a new external API.

from typescript.

antoinerousseau avatar antoinerousseau commented on March 29, 2024

Why has this issue been closed?

from typescript.

 avatar commented on March 29, 2024

@antoinerousseau, this issue is not closed ❓❕

from typescript.

antoinerousseau avatar antoinerousseau commented on March 29, 2024

oops yes sorry I got confused with the Closed label next to the #3503 reference just above

from typescript.

bryanrideshark avatar bryanrideshark commented on March 29, 2024

@mhegazy, you mentioned that it would be difficult to do, assuming that you weren't just looking for white space removal, and variable name shortening.

In my opinion, it is the second item in your list which would be the most beneficial. In our pipeline, we use UglifyJS to handle minification of TypeScript output, and it handles the AST optimization pretty well. What it can't handle, that Typescript can handle, is the second item you listed as a simpler task (variable name shortening.)

How could someone get more involved in that area of the project? Introducing variable name shortening as an option, would in my opinion be a huge piece of the pie, as that's the area where type definitions are required the most.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

@bryanerayner we are currently looking into updating the implementation the emitter to allow for a transformation pipeline. no ETA at the point, but when this happens emitter changes should be much simpler to do.

I worry that a set of options to partially minify will not solve the original issue, but will introduce new complexities for users and in the code base. if this is something that you find interesting, feel free to look at the compiler sources and the API, and provide a proposal for how you intend it to work.

from typescript.

 avatar commented on March 29, 2024

@mhegazy, so theoretically speaking, given the compiler is now exposed for third-party/external extensibilities, we can get a hook into AST and then implement the ultimate minification possible with all the trans-compilation context info. In this case, minification should be a feature of tsc, so consumers of TS should not rely on the external module (uglifyjs etc.).

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

@jasonwilliams200OK i agree. i believe the request here is for tsc to subsume your minifier in the tool chain. i do not think you would want to run tsc --minify then run your minifier again.

from typescript.

electricessence avatar electricessence commented on March 29, 2024

++

from typescript.

 avatar commented on March 29, 2024

I do not understand why those things in ts 1.7 roadmap can be more important than minify.

Whaaaat? "Colorization of JSX code in VS 2015" (#4835) is NOT important than minification? 😜

Agree minification deserves some love. Not only shortening of names and elimination of whitespace but also some deep optimizations, joining of scattered file content etc. too. But naturally this is a complicated task which can be carried out in phases; basic->advanced.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@kitsonk, there are some problems with using closure and uglify that have been discussed above.

from typescript.

electricessence avatar electricessence commented on March 29, 2024

Maybe I'm missing something, but I've been having significant difficulty in getting sourcemaps and uglify/minify to work as expected. What I really want is a TSC.exe compiler option to minify the file with sourcemaps. IMO, simply stated, if you are using AMD or any other module based code, you really don't care about how the resultant JS looks because you have the sourcemaps.

Here's what I've found (spend hours and hours on this):

  1. tsc with sourcemaps works perfectly without minification/uglify.
  2. gulp-typescript + gulp-sourcemaps generates different sourcemaps than 1)
  3. Any attempt at rendering minified files externally using gulp-sourcemaps, say in a 'min' folder creates strange references that don't resolve without overriding the sourceRoot with a function.

Ultimately, the above basically says there are bugs in the gulp plugins, but I would be happy if simply my resultant JS files were minified straight out of the TypeScript compiler.

from typescript.

electricessence avatar electricessence commented on March 29, 2024

Also, it would be neat if you could replace the __extends code with an AMD module called 'extends' or something. :) Could take a chunk out of the total code.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

@mhegazy Is there any way with 1.7 to control or get access to the text writer or emitter? I just want to do a bit of experimenting with whitespace removal and private identifier mangling.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

For anyone who is interested.. I've experimented with walking the container nodes and minimizing blockscopedvariables. This requires a minimal amount of code, so I'm going to add it to TsProject on NPM and GitHub . It will work only on bundles.
As for whitespace removal, there is currently no way to change the way that indentation works in the js text writer. However, It is also a simple task to use the Typescript Scanner in a post emit step to remove all whitespace trivia and newline trivial.

from typescript.

bryanerayner avatar bryanerayner commented on March 29, 2024

Fantastic! That's a great first step.

On Sat, Dec 12, 2015, 5:08 PM Todd Thomson [email protected] wrote:

For anyone who is interested.. I've experimented with walking the
container nodes and minimizing blockscopedvariables. This requires a
minimal amount of code, so I'm going to add it to TsProject. It will work
only on bundles.
As for whitespace elimination, there is currently no way to change the way
the indentation works in the js text writer, so this will have to be done
as a processing step after the bundle emit.


Reply to this email directly or view it on GitHub
#8 (comment).

from typescript.

richmwatts avatar richmwatts commented on March 29, 2024

Can someone please clarify if minification of the TypeScript output is likely to be problematic using tools such as uglify? Some of the files in a project I'm working on seem to minify correctly while others do not.

from typescript.

am11 avatar am11 commented on March 29, 2024

@RichWatts, generally it should not be problematic but sometimes configuring the uglify is bit unpalatable. I recently had issues with jQuery and some other (non-AMD) plugins in the mix. I was using gruntfile.js to produce single minified file. Here is the how the configuration looks like:

(gruntfile.js in the root of simple 'website' project)

/// <binding ProjectOpened=uglify/>

// the above line is produced & parsed by VS2015's Task Runner Explorer for
// binding the grunt/node.js task with EnvDTE events.

module.exports = function (grunt) {
  require('load-grunt-tasks')(grunt); 
  var jsBinBase = 'assets/bin/'; 

  grunt.initConfig({
    uglify: { 
      options: { 
        compress: true, 
        mangle: { 
          except: ["$super"], 
          toplevel: true 
         }, 
        sourceMap: true 
       }, 
       target: { 
         src: [ // mixed sources, AMD and non-AMD
           'assets/js/rickshaw/vendor/d3.v3.js',  // needs to be in order
           'assets/js/jquery-2.1.1.js', // needs to be in order
           'assets/js/jquery-ui/jquery-ui.js',  // needs to be in order
           'assets/js/**/*.js', // include all js files generated by tsc or otherwise
           '!assets/bin/**'  // this is exclusion
         ], 
         dest: jsBinBase + 'app.min.js' 
       } 
     }
  });

  grunt.registerTask('default', ['uglify']);
};

(package.json side by side with grunt.js)

{ 
   "version": "0.0.1", 
   "name": "Dummy", 
   "private": true, 
   "devDependencies": { 
     "grunt": "^0.4.5", 
     "grunt-contrib-uglify": "^0.11.0",
     "load-grunt-tasks": "^3.3.0" 
  } 
} 

As you can see, i had to hand pick some dependencies in targets to make it work. But in case you are using everything-AMD, you wouldn't need to care about the targets order.

If you have similar setup, you can generate the combined output from tsc with source maps and then in the above grunt example change sourceMap: true to sourceMap: 'path/to/your-output.js.map'. With that, uglify will generate mapping between min file and the actual ts file (not just min.js to js), which comes handy when debugging with browser dev tools.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@RichWatts, uglify will not minify private methods.

from typescript.

 avatar commented on March 29, 2024

@NoelAbrahams, private method has nothing to do with uglify.

Private visibility is a design-time construct; it is enforced during static type checking but does not imply any runtime enforcement. ... TypeScript enforces encapsulation of implementation in classes at design time (by restricting use of private members), but cannot enforce encapsulation at runtime because all object properties are accessible at runtime. Future versions of JavaScript may provide private names which would enable runtime enforcement of private members

He (@RichWatts) is not uglifying a "typescript file" but the "typescript output", which is JavaScript file..

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@jasonwilliams200OK, well yes. I think most people on this forum understand private methods without recourse to a quote.

from typescript.

electricessence avatar electricessence commented on March 29, 2024

^^ yes! Exactly. :)

from typescript.

 avatar commented on March 29, 2024

@NoelAbrahams, while I agree that TypeScript has better context to improve the compression, the private methods issue you are describing is non-existent with UglifyJS if you use the correct switches:

npm install uglify-js

:: with --mangle-props
.\node_modules\.bin\uglifyjs --mangle --mangle-props -- yourexample.js
var Foo=function(){function o(){}o.prototype.a=function(){this.b()};o.prototype.b=function(){};return o}();
:: ^107 chars

:: without --mangle-props
.\node_modules\.bin\uglifyjs --mangle -- yourexample.js
var Foo=function(){function t(){}t.prototype.myPublicMethod=function(){this.myPrivateMethod()};t.prototype.myPrivateMethod=function(){};return t}();
:: ^148 chars

from typescript.

eggers avatar eggers commented on March 29, 2024

but, don't you want publicMethod not to be mangled?

from typescript.

 avatar commented on March 29, 2024

@eggers, then you use an additional switch --mangle-regex="/Private/".
As a library author, if you are using some convention like underscrore before/after the private properties names, then this option is for you.

from typescript.

kitsonk avatar kitsonk commented on March 29, 2024

Which is why it is best left to tools that are designed to minify. While TypeScript might have more context, there has been a lot put into the minfication tools to make them work in the myriad of use cases. The only improvement I can really see is that it "intelligent" minification is built into a build pipeline that utilises the language services of tsc to provide some guidance. Integrating that directly into TypeScript though seems to be an anti-design goal:

  1. Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.

as well as:

  1. Aggressively optimize the runtime performance of programs. Instead, emit idiomatic JavaScript code that plays well with the performance characteristics of runtime platforms.

Which is why I guess I am still surprised @RyanCavanaugh is suggesting it.

from typescript.

 avatar commented on March 29, 2024

@kitsonk, I agree.

postcss is an AST for CSS. Any CSS utility (compiler, compressor, linter) which implements postcss gives the free advantage of integrating all the other utilities built on top of postcss via piping. This integration is not "just" pipe contract either; they can share the context (the actual AST!) with each other.

I wonder if there is any general purpose AST built for JavaScript, which TypeScript, CoffeeScript, Uglify, SweetJS, LiveScript, JSHint, JSCS etc. can get compliant with to be able to "share the context" with each other and yet provide legendary experience -- as opposed to each implementing redundant features in different ways which BTW also goes against the DRY and single responsibility principles.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

Unfortunately, this discussion has been sidetracked by spurious arguments (by those, I suspect, not really familiar with real-world minification problems).

As I said above

uglify cannot [minify private methods] _(without further annotation)_

Without asking owners of code-bases spanning tens of thousands of LOC to carry out special annotation, there is _no tool in the present evironment_ that can effectively minify _TypeScript generated_ code.

A number of real-world problems have been discussed above, such as dealing with properties that are bound to the DOM (which shouldn't be minified). TypeScript can make a difference here. Please do take the time to read through the comments.

We are at present facing a script that is approaching many hundreds of KB and this feature suggestion remains critical for us.

from typescript.

 avatar commented on March 29, 2024

I am well familiar with real-world minification problems. I am simply explaining what is possible with Uglify today.

Please do take the time to read through the comments.

Have you read mine? I am not against this proposal.

Please speak for yourself only and don't be sarcastic. So we are sharing thoughts here and this conversation is well on topic (Uglify is relevant).

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@jasonwilliams200OK, sorry, no offence intended. I do urge you to stop making impractical suggestions, though 😄.

from typescript.

kitsonk avatar kitsonk commented on March 29, 2024

Unfortunately, this discussion has been sidetracked by spurious arguments (by those, I suspect, not really familiar with real-world minification problems).

That was a bit rude and uncalled for, and rather ill-informed.

I am very familiar with real-world minification challenges. Also you stated:

It is therefore likely that many library developers are put off from using TypeScript because of these inefficiencies.

I can tell you, as a CTO of a major company behind one of the largest open source JavaScript libraries hasn't dissuaded us from TypeScript. Maybe you should do a bit of research before you toss out throw away comments like that.

You originally stated:

uglify will not minify private methods.

@jasonwilliams200OK indicated that you could, providing a working example, which you dismiss out of hand. How does that become?

I do urge you to stop making impractical suggestions, though

Even with a smiley, that is uncalled for...

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

Congratulations on your job.

The first suggestion was impractical, because it also minifies public methods. The second suggestion is intrusive, because it involves annotating private methods and/or following some sort of convention, such as an underscore prefix.

from typescript.

 avatar commented on March 29, 2024

You are seeing it as intrusive, I take it as room for improvement in Uglify, on which many users rely today. Turned out I am not alone as I have found this: mishoo/UglifyJS#103. See the comments by @jrhite. It is possible to control what properties to mangle in uglify today with programmatic usage, though further improvements might be required as the issue is still opened. TypeScript team can still take dependency on UglfiyJS in implementing this feature by injecting the context and controlling the internal steps.

Like i mentioned earlier, if TypeScript support some common AST core with foreign interface like postcss provides for CSS (so you can plug/swap minifier of your choice or any utility) and LLVM provides for native langs, then these features can be provided more effectively in a modular and extensible manner.

Please do implement this feature, preferably by engaging Uglify.

To sum up, we can achieve the same goal with one of the following approaches:

  • Take hard dependency on Uglify and package it with typescript. Less flexible, simplest to implement and simple to use.
  • Provide consumer to control annotation in generated code so they can make choice on taking dependency on Uglify or something else. Conversely, typescript to always annotate the private methods with fixed symbol. Flexible enough, simple to implement and use.
  • Be the first to provide intermediate AST core tier for convergence such as postcss. Very flexible and extensible, hard part is to convince everyone to converge to a common interface.
  • Implement the minification feature from scratch. This will take couple of iterations to become as good as Uglify, but eventually it will work too.

@NoelAbrahams, no problem. Same goal, different approaches. Like i said earlier, we are sharing thoughts and I provided information on what can be achieved today. Final decision on how will come from TypeScript team.

from typescript.

jrhite avatar jrhite commented on March 29, 2024

@jasonwilliams200OK - I have no skin in this, but just an update. mishoo/UglifyJS#103 should be closed as full support is already there. Additionally, another feature was added which allows very useful control over which properties do and don't get mangled. Check the uglify2 docs for more info.

--mangle-regex Only mangle property names matching the regex

Cheers!

from typescript.

DanielRosenwasser avatar DanielRosenwasser commented on March 29, 2024

I know everyone's looking for the best direction for the project, so let's keep that in mind and try to keep the discussion civil and driven by the issue at hand.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@jasonwilliams200OK,

I think your suggestion of TypeScript producing JavaScript output annotated in a language that Uglify (and perhaps other minifiers) can understand is probably the easiest route forward at present. This would work for us as we already use Uglify for our minification.

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on March 29, 2024

Which is why I guess I am still surprised @RyanCavanaugh is suggesting it.

@kitsonk TypeScript issues 1 through 22 were manually ported by me from the old CodePlex site based on number of votes they had at the time. I don't really remember why we picked that number.

Honestly for all the comments that have been logged here, I'm shocked that there doesn't seem be be a reference implementation based on some very straightforward usage of the language service.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

@RyanCavanaugh,

Honestly for all the comments that have been logged here, I'm shocked that there doesn't seem be be a reference implementation based on some very straightforward usage of the language service.

If the TypeScript team is not going to implement this feature then I suggest this issue be closed. That may motivate someone to take this up. It would also help us in the decision to consider looking for alternatives.

This was one of the earliest issues raised (7th of October 2012 - just a few days after TypeScript went public), received a large number of votes before being moved to GitHub (6th in terms of number of votes), and three years later there has been no resolution.

from typescript.

NoelAbrahams avatar NoelAbrahams commented on March 29, 2024

If someone has waited three years without looking for an alternative, they apparently don't need minification all that badly

In our own case, this was initially not a big concern, because the code-base was smaller. But as we deal with more feature requests, each accompanied by growth in code, this is becoming critical.

If no one is bothering

My point is that so long as this feature is open, the expectation is that TypeScirpt will do something about it.

we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers,

I agree that something more than simply minification of private methods would be required for TS to implement minification entirely by themselves. But providing output that Uglify can understand might be a start. I might have a go at providing more justification, but can't promise anything at the moment due to work commitments.

Our current belief is that this might be possible, but is almost certainly a huge amount of work that we could spend in more productive ways

Not sure how "a reference implementation based on some very straightforward usage of the language service" suddenly turned into a "huge amout of work" 😃

from typescript.

ivogabe avatar ivogabe commented on March 29, 2024

With a transform based compiler (#5595) this would probably be a lot easier to implement in the compiler or using an external project.

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024

[apologies for the wall of text]

I think Ryan's suggestion that the state "NeedsProposal" means "it's not yet been made clear how to do this in a way that's a clear win over alternatives" is perfectly reasonable. There does need to be some way to express that concept.

I can speak a little to whether the current state of affairs is "good enough". Our current deployment process produces two output scripts, one of which is fairly small (~45k uncompressed) but highly size-sensitive because it's loaded in customers' pages, the other being larger (~500k uncompressed) and growing rapidly as we add features. These are produced via tsc --out [...] and closure --compilation_level=SIMPLE_OPTIMIZATIONS. I've tested various options through uglifyjs and gotten similar output, so I think we can safely assume this is close to the best we can do without type-aware optimization. This is "good enough" for the time being, but it's going to become a problem eventually. The issues we're likely to run into are download time over slow networks; but more importantly, parse time on mobile devices. The latter can be significant because parsing blocks the UI thread, and happens every time the script's evaluated (i.e., the parsed output is in-memory only, and thus not cached).

Eyeballing the final output and comparing it to the GWT output I pasted above (or any code run through either GWT or closure with "advanced" optimizations), I'd be willing to bet that we could cut these files by about 40% pre-gzipping. That's a bit of a wild-assed guess, but with the enormous number of unmangled identifiers in the output, along with the lack of inlining and other relatively simple optimizations (c.f. https://www.fullstory.com/s/fs.js), I doubt it's far off. And that kind of improvement would give us a lot more headroom for future development.

So, what's required to get this level of optimization? Reliable type information. If you look at the approach that tools like uglifyjs uses, e.g. to mangle property names, you'll see that it's kind of a hack (which is the best you can do without type information) -- you have to give it a list or pattern of identifiers that are "safe" to rename, and if you miss anything the output will be broken in unpredictable ways. OTOH, if you look at either the GWT or Closure (in "advanced" mode) compilers, you'll see that they carefully track the types of all expressions. This allows them to know not just which identifiers are private and can be renamed, but also which are external to the program (e.g., if I write window.ExposedThing = internalFunction, I need to know both that I can mangle internalFunction, and not ExposedThing).

I've considered digging into tsc to see whether it's feasible to do something similar, but have put it off both because of time constraints, and because I'm not certain whether it's compatible with the team's goals. My biggest concern is that there seems to be more of a focus on single-file compilation and working with the various --module options, than there is on monolithic compilation using ///<reference> directives. But producing globally-optimized output conflicts to a certain degree with single-file compilation. If anyone on the team could offer some insight into whether continuing to improve monolithic compilation is an explicit goal, that would help me decide whether to devote time to the problem (and recruit others I know using Typescript to contribute to optimizations).

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024

It's probably worth roughly sketching out what I think could work in the context of Typescript:

Because the language allows untyped expressions, and that they're fairly common, it's likely unrealistic to take quite the same approach as when compiling a much stricter source language like Java. Because the GWT compiler can rely on knowing the precise (and generally singular or very small) set of types for each expression, it can be extremely aggressive in renaming identifiers and allocating those names. Referring to the output fragment I pasted above, you'll see lots of things like:

function abc(a,b) { if (a.a == b.a) {b.b(a, b); }
// ...

Outside of external references, you'll see essentially no human-readable identifiers, and the names are mostly linearly allocated in small scopes (so you get lots of a, b, ..., aa, ab, etc). If you're careful about sorting the output, you can even get significant improvements in gzip performance by reducing entropy within the compression window.

With less-reliable type information, I suspect it would be much easier to rely on a single scope for allocating mangled identifiers (with the possible exception of function parameters, which most optimizers already rename locally). So in essence you'd end up with a single "rename dictionary" which would contain all the identifiers that can be safely renamed, such that all instances of someIdentifier get renamed to abc, regardless of their scope. This is sub-optimal, but works fairly well in practice (I believe most products that use Closure do so with the set of flags that causes it to work this way, or at least did as of a few years ago).

An identifier would be considered "rename safe" if the scopes of all references to it can be determined to originate within Typescript source. Any reference via an expression of unknown type (e.g., bar in var foo = untypedFunc(); foo.bar()) or an explicitly external type (e.g., via some_lib.d.ts) would be excluded from renaming. This works to the extent the code contains explicit or inferred type information, and removes the need for an explicit "externs" file, which is very error-prone.

It would be nice to get function/method inlining working as well, but it has stricter requirements. To get much benefit from it, you need to be able to precisely identify all references to the function. Otherwise you can't remove it. This may sound like a small win, but in code with lots of trivial accessor methods, it can really add up.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

I'll be posting the minification feature to TsProject this coming week. I am working out the last bits of the implementation for ES5 vs ES6 ( block scoped identifiers using let ).

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

Here is an example of minifier in TsProject ( whitespace elimination turned off ).
Source:

export class TestClass {

    private gerb1: number = 1;
    private gerb2: number = 2;
    private gerb3: number = 3;
    private gerb4: number = 4;

    public fPublic(): number {
        let result = this.f1();

        return result;
    }

    private f1(): number {
        let result = this.f2( 5, 2 ) + this.f3( 19 );

        return result;
    }

    private f2( parm1: number, parm2: number ): number {
        let result = parm1 + parm2;

        return result;
    }
    private f3( parm1: number ): number {
        return parm1 + this.f2( 1, 2 );
    }
}

var test = new TestClass();
var t1 = test.fPublic();
console.log( t1 );

min.js output...

var TestClass = (function () {
    function TestClass() {
        this.a = 1;
        this.b = 2;
        this.c = 3;
        this.d = 4;
    }
    TestClass.prototype.fPublic = function () {
        var a = this.e();
        return a;
    };
    TestClass.prototype.e = function () {
        var a = this.f(5, 2) + this.g(19);
        return a;
    };
    TestClass.prototype.f = function (a, b) {
        var c = a + b;
        return c;
    };
    TestClass.prototype.g = function (a) {
        return a + this.f(1, 2);
    };
    return TestClass;
})();
exports.TestClass = TestClass;
var a = new TestClass();
var b = a.fPublic();
console.log(b);

from typescript.

ivogabe avatar ivogabe commented on March 29, 2024

@ToddThomson Can you compare and post the minified (without whitespace elimination) gzipped file size with the normal gzipped size? Gzip stores a long identifier name that is used multiple only once, so I suspect that mangling private identifiers won't make a big difference.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

@ivogabe I'll post statistics when the minifier feature is production ready. I suspect that there won't be great gains over post compilation gulp build tools like uglifyjs. However, my goal is to be able to realize some gains by shortening private identifiers and knowing that the resulting transformed sourcefile will work 100% of the time.

from typescript.

bryanerayner avatar bryanerayner commented on March 29, 2024

Entropy can be reduced by mangling the names of private variables.
If you have 25 instances of separate long names, this will be 25 different new symbols that need to be put into the Huffman table.
Comparatively, 25 instances of this.a would only result in one key being added to the Huffman table (a),

I expect the compression of the latter case would be much higher.

This is great work Todd!

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024

@ToddThomson Glad to hear you're working on this! A couple of quick questions:

  • If I understand correctly, this minification pass is meant to operate on a single compilation unit. Is that correct?
  • Is that branch public, or likely to be soon?

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

@joelgwebber Yes, the main reason I use TsProject is to produce a single bundled output for multiple ES6 modules in separate source files. Yes, it is a public GitHub repository. There's a link above to the TsProject bundle minification issue that will take you to the repository.

from typescript.

joelgwebber avatar joelgwebber commented on March 29, 2024

Thanks, @ToddThomson. Got a 404 for that branch, but I see now that it's been merged.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

@joelgwebber , @bryanerayner I've published TsProject 1.2.0-rc.3 to NPM. It now includes proper handling of class inheritance. I've also added a sample minified typescript app to the GitHub repository.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

@RyanCavanaugh I'm not sure how my basic support for identifier minimizing and whitespace elimination can be brought to Typescript. It appears to me that what I've provided in TsProject would be easily achievable within Typescript, but it is something that should be designed and implemented by the core group.

from typescript.

ToddThomson avatar ToddThomson commented on March 29, 2024

Last post concerning minification in TsProject. With the latest NPM release, I've built TsProject.min.js with full whitespace elimination. I also took the non-minified tsproject.js and ran it through the online JSCompress tool: Here are the results:
TsProject.js 110,908
TsProject.min.js 42,125
TsProject.js through jscompress online: 47,940.
TsProject.js through Uglify online: 42,722
Not startling results, but a pretty good start.

Final update - With non-external functions, classes and import namespace aliases minified:

TsProject.js 132,963
TsProject.min.js 40,389

TsProject.min.js is a node package built using TsProject.min.js.

from typescript.

mmacfadden avatar mmacfadden commented on March 29, 2024

Just to pile on to this issue... We are currently starting a new JS project and are primarily trying to decide between TypeScript and the Google Closure Compiler. Both offer the ability to add static typing to JavaScript (ish) applications. In my view, TypeScript has much better tooling and is in general a more pleasant development experience. It's integration with IDE's is good, the compiler is much faster than closure, the syntax is better (less verbose), and to tool chain is better.

However, we really do like the ADVANCED_COMPILATION mode. It can:

  • More aggressively minify based on type information.
  • Essentially make private methods unusable through renaming
  • Makes it harder to reverse engineer

It does significantly better than using something like UglifyJS on transpiled TypeScript. The files output by the closure compiler are significantly smaller. Although I a no expert in how this is achieved, as others have mentioned it seems to be that the fact that the "minifier" in closure is type-aware allows it to do more aggressive minification. UglifyJS (and the like) do not have this benefit. Doing optimizations and minification inside the type script transpiler should allow it to outperform "bolt on" minifiers.

I am not sure if we will use Closure or TypeScript, but I can say that this particular issue is one of the main decision points for us.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

@mmacfadden, I do not see this as an either/or. I know of many teams including Google Angular 2 team that use Typescript + Google Closure Compiler advanced optimizations and are satisfied by the results. You will need something like https://github.com/angular/sickle to convert TS types into JSDoc comments.

from typescript.

mmacfadden avatar mmacfadden commented on March 29, 2024

@mhegazy, thanks for the reply we had seen a few TS -> closure projects out there but they seemed very out of date. Although I had searched, I had not come across this project before. It may be a viable option. I have two comments:

  1. I do get worried with projects like this only because there are now three things that have to work well together in order for my build to work. If type script adds a new (relevant) feature, it then needs to be added to sickle. It also then needs to be something that actually is possible to do in closure. The Sickle developers are (perhaps) not on the TypeScript team nor the Closure team, so there is some risk there that at some point in the future the two will not play nice together. Or that the Sickle tool becomes unmaintained. There is just some risk there.
  2. From a build complexity standpoint I now have two things I need to wrangle instead of one. Not a big deal, but still a small consideration.

So if I had a magic wand I would love to see this capability right in TypeScript. I do realize, being a developer myself) that the TS team needs to focus on their sweet spot of functionality and that building a Type Aware minifcation system probably is slightly out of their sweet spot. I am totally willing to take a look at Sickle and if it works... great, I think my immediate issue will be solved. But again, assuming a magic wand, I would still love to see this in TS directly.

Again thanks for pointing this project out. I will definitely take a look.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

type aware minification is not all what advanced optimizations in closure compiler. if that is the only thing you need @ToddThomson's work should do the trick.

from typescript.

mmacfadden avatar mmacfadden commented on March 29, 2024

You are correct. I am looking for more than just Type Aware minification. Being type aware does let you do some other interesting optimizations, but you are correct there are more things that it does. I am also interested in what @ToddThomson is doing.

from typescript.

raphaelfeng avatar raphaelfeng commented on March 29, 2024

Hi @mhegazy, would like to know how the angular 2 team use the TypeScript together with Google Closure Compiler. Looks that the uglify is used in the angular2 github repository https://github.com/angular/angular/blob/4a414420e940bf4a5eea2c870ddff6e33f0343b9/gulpfile.js#L1273.

from typescript.

mhegazy avatar mhegazy commented on March 29, 2024

//CC @mprobst from the angular team to answer this question.,

from typescript.

mprobst avatar mprobst commented on March 29, 2024

@mmacfadden @raphaelfeng we're still experimenting around, there's no definite answer from our side yet. We're using uglify and seeing good results with that.

We're also building sickle and a compilation toolchain that pipes from TypeScript through Closure Compiler. We hope to possibly give better optimization results from that than uglify itself - mostly due to property renaming, dead code elimination, but also inlining, type specialization, and a host of other tricks. However it is true that it will cause a more complicated build setup expect (e.g. users will need to install Java), and issues when the two tools might not agree. It's hard to know if the end result will be nice to use before trying.

We don't quite know yet where this will lead, but will keep people posted. Keep an eye on https://github.com/angular/sickle for our progress.

from typescript.

mmacfadden avatar mmacfadden commented on March 29, 2024

@mprobst, thanks! I am optimistic that something like sickle could be more successful because the angular team and the typescript teams are working closely together and also because Closure is a Google product. So it's not as though some independent and unrelated folks are trying to do the work. Definitely interested in sickle and will monitor the project.

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.