Uglifyjs: mangling breaks Angular 2

Created on 5 Mar 2016  路  22Comments  路  Source: mishoo/UglifyJS

Creator of Gulp Uglify told us to make an issue here about mangle which if set to true breaks Angular 2 for some reason. The error i get personally is "maximum calls stack size exceeded...".

here is the github issue link

https://github.com/terinjokes/gulp-uglify/issues/168#issuecomment-192541344

Most helpful comment

It's not reasonable for Uglify devs to sort through a half dozen foreign build tools and libraries to find someone's error. A proper bug report would be to produce a single javascript file with the uglifyjs CLI command to transform it and then detail the exact error produced - whether produced by uglify, node or the browser. If we are to fix a bug, some effort has to be made on the part of the bug reporter to isolate the problem.

All 22 comments

Could you please describe your specific problem here?

It's not reasonable for Uglify devs to sort through a half dozen foreign build tools and libraries to find someone's error. A proper bug report would be to produce a single javascript file with the uglifyjs CLI command to transform it and then detail the exact error produced - whether produced by uglify, node or the browser. If we are to fix a bug, some effort has to be made on the part of the bug reporter to isolate the problem.

@kzc Um the link i gave does have an example Here is carnun words from the other post

SO Link

"In order to recreate the issue clone this code - https://github.com/sudoorgza/my-words/tree/uglify-mangle-issue. Then do an "npm install", run "gulp build" then deploy the code in the "dist/" folder to your favourite webserver."

This is Carnun problem, the proposed solution on SO worked for me too, which leaves us to my own specific problem which is what i described "maximum calls stack size exceeded...".

to recreate the problem please clone my ng2-boilerplate repo and follow the instruction to set it up, after that go to gulpfile.js and remove from jsuglify {mangle: false} and try to run the server again

ng2-boilerplate

i am sorry but isolating exactly where the bug happens is beyond me, i just thought it would be good to give you heads up here as in the future i am sure more people will complain about it. I understand your position but thats as far as i can go as i do not know how to isolate the error.

Repeating the same instructions is not helpful in diagnosing the possible bug.

As requested, please provide a single javascript source file that has not been uglify'ed, as well as the uglifyjs CLI flags that creates the problem in addition to the exact error produced. We do not wish to run a web server and search for some unknown problems with the javascript and/or html of your project. We only wish to have the raw inputs to uglify without third party tools or libraries and an explanation of what you believe uglify is doing wrong.

FYI - doesn't appear to be an uglify problem - some angular apps make assumptions about symbol naming.

"AngularJS applications are not min-safe by default"

http://www.sitepoint.com/5-minutes-to-min-safe-angular-code-with-grunt/

Redirect follow up questions to Angular or Grunt projects.

FYI - thats for Angular 1 not Angular 2.

See angular compress flag: added @ngInject support for inline functions #482

Beyond that you'd have to ask the Angular devs how to avoid minification problems with their code introspection/injection tools.

this is a problem that happens when UglifyJS2 is minifying an ES6 classes that were compiled to ES5. here's the repo that I used https://github.com/gdi2290/angular2-seed-RouterLink-error
(in the repo if there are no links then there's a problem with the RouterLink class)
notice it's c = () => function t(){}

var c = function() { 
  function t(t, e) {}
  return t;
}();

if you manually change t to c it works
with uglify

webpackJsonp([ 2 ], {
    0: function(t, e, r) {
        "use strict";
        r(126);
    },
    126: function(t, e, r) {
        "use strict";
        var i = this && this.__decorate || function(t, e, r, i) {
            var n = arguments.length, o = n < 3 ? e : null === i ? i = Object.getOwnPropertyDescriptor(e, r) : i, a;
            if ("object" === typeof Reflect && "function" === typeof Reflect.decorate) o = Reflect.decorate(t, e, r, i); else for (var u = t.length - 1; u >= 0; u--) if (a = t[u]) o = (n < 3 ? a(o) : n > 3 ? a(e, r, o) : a(e, r)) || o;
            return n > 3 && o && Object.defineProperty(e, r, o), o;
        };
        var n = this && this.__metadata || function(t, e) {
            if ("object" === typeof Reflect && "function" === typeof Reflect.metadata) return Reflect.metadata(t, e);
        };
        var o = r(5);
        var a = r(1);
        var u = r(56);
        var s = r(77);
        var c = function() {
            function t(t, e) {
                var r = this;
                this._router = t;
                this._location = e;
                this._router.subscribe(function(t) {
                    return r._updateLink();
                });
            }
            t.prototype._updateLink = function() {
                this._navigationInstruction = this._router.generate(this._routeParams);
                var t = this._navigationInstruction.toLinkUrl();
                this.visibleHref = this._location.prepareExternalUrl(t);
            };
            Object.defineProperty(t.prototype, "isRouteActive", {
                get: function() {
                    return this._router.isRouteActive(this._navigationInstruction);
                },
                enumerable: true,
                configurable: true
            });
            Object.defineProperty(t.prototype, "routeParams", {
                set: function(t) {
                    this._routeParams = t;
                    this._updateLink();
                },
                enumerable: true,
                configurable: true
            });
            t.prototype.onClick = function() {
                if (!a.isString(this.target) || "_self" == this.target) {
                    this._router.navigateByInstruction(this._navigationInstruction);
                    return false;
                }
                return true;
            };
            t = i([ o.Directive({
                selector: "[routerLink]",
                inputs: [ "routeParams: routerLink", "target: target" ],
                host: {
                    "(click)": "onClick()",
                    "[attr.href]": "visibleHref",
                    "[class.router-link-active]": "isRouteActive"
                }
            }), n("design:paramtypes", [ u.Router, s.Location ]) ], t);
            return t;
        }();
        e.RouterLink = c;
    }
});
//# sourceMappingURL=no_uglify.bundle.js.map

no uglify

`````` es6
webpackJsonp([2],{

// 0:
/
/ function(module, exports, __webpack_require__) {

// import 'angular2/core';
// import 'angular2/platform/browser';
// import 'angular2/src/router/router';
// import 'es6-shim';
// import 'es6-promise';
// import 'zone.js/dist/zone-microtask';
// import 'zone.js/lib/browser/zone-microtask';
// import 'reflect-metadata';
// import 'es7-reflect-metadata';
"use strict";
__webpack_require__(126);
// import 'angular2/src/core/change_detection/parser/lexer';
// import 'angular2/src/compiler/template_ast';
// import 'angular2/src/compiler/html_lexer';
// import 'angular2/src/compiler/template_compiler';

/*/ },

// 126:
/
/ function(module, exports, __webpack_require__) {

'use strict';var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var core_1 = __webpack_require__(5);
var lang_1 = __webpack_require__(1);
var router_1 = __webpack_require__(56);
var location_1 = __webpack_require__(77);
/**
 * The RouterLink directive lets you link to specific parts of your app.
 *
 * Consider the following route configuration:

 * ```
 * @RouteConfig([
 *   { path: '/user', component: UserCmp, as: 'User' }
 * ]);
 * class MyComp {}
 * ```
 *
 * When linking to this `User` route, you can write:
 *
 * ```
 * <a [routerLink]="['./User']">link to user component</a>
 * ```
 *
 * RouterLink expects the value to be an array of route names, followed by the params
 * for that level of routing. For instance `['/Team', {teamId: 1}, 'User', {userId: 2}]`
 * means that we want to generate a link for the `Team` route with params `{teamId: 1}`,
 * and with a child route `User` with params `{userId: 2}`.
 *
 * The first route name should be prepended with `/`, `./`, or `../`.
 * If the route begins with `/`, the router will look up the route from the root of the app.
 * If the route begins with `./`, the router will instead look in the current component's
 * children for the route. And if the route begins with `../`, the router will look at the
 * current component's parent.
 */
var RouterLink = (function () {
    function RouterLink(_router, _location) {
        var _this = this;
        this._router = _router;
        this._location = _location;
        // we need to update the link whenever a route changes to account for aux routes
        this._router.subscribe(function (_) { return _this._updateLink(); });
    }
    // because auxiliary links take existing primary and auxiliary routes into account,
    // we need to update the link whenever params or other routes change.
    RouterLink.prototype._updateLink = function () {
        this._navigationInstruction = this._router.generate(this._routeParams);
        var navigationHref = this._navigationInstruction.toLinkUrl();
        this.visibleHref = this._location.prepareExternalUrl(navigationHref);
    };
    Object.defineProperty(RouterLink.prototype, "isRouteActive", {
        get: function () { return this._router.isRouteActive(this._navigationInstruction); },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(RouterLink.prototype, "routeParams", {
        set: function (changes) {
            this._routeParams = changes;
            this._updateLink();
        },
        enumerable: true,
        configurable: true
    });
    RouterLink.prototype.onClick = function () {
        // If no target, or if target is _self, prevent default browser behavior
        if (!lang_1.isString(this.target) || this.target == '_self') {
            this._router.navigateByInstruction(this._navigationInstruction);
            return false;
        }
        return true;
    };
    RouterLink = __decorate([
        core_1.Directive({
            selector: '[routerLink]',
            inputs: ['routeParams: routerLink', 'target: target'],
            host: {
                '(click)': 'onClick()',
                '[attr.href]': 'visibleHref',
                '[class.router-link-active]': 'isRouteActive'
            }
        }), 
        __metadata('design:paramtypes', [router_1.Router, location_1.Location])
    ], RouterLink);
    return RouterLink;
})();
exports.RouterLink = RouterLink;

/*/ }

});
``````

the current workaround is to tell uglify not to minify the name of classes that throw errors when methods aren't found on the prototype

@gdi2290 so i guess that the current workaround is what i used, mangle: false?

EDIT: looking at your response on issue6380 guess i will use that if it does the trick i guess.

@gdi2290 Unless I'm missing something, the uglify mangled code displayed above looks like it was done correctly from an ES5 point of view given the original JS code displayed below it.

Is the issue here that upon throwing an exception Angular introspects the stack trace and is looking for the function name "RouterLink" which is now mangled to the name t? If so, the only possible workaround is exclude RouterLink from mangle as you've suggested.

@kzc the problem seems to happen when there are many components with the same name t (t is the first variable name used by uglify in a new js scope) this is because of the code generation work in Angular 2 where the templates are compiled into factory functions.

cc @IgorMinar @tbosch

But from uglify's perspective this inner function can be renamed t or whatever uglify wants as long as it is referenced correctly/unambiguously within its scope. It is the responsibility of the user of uglify to inform it to not mangle specific names (or disable mangle altogether), or have Angular associate the function with a string literal of the original function name. Even though all these functions in different scopes may be named t, the function references are not === to each other.

Sorry, I meant to say after looking into it further I determined that it's a problem with Angular 2 and it's use of the component names during code generation. I've cc'd members of the core team who are working on this problem to clarify anything if I'm not on the right track.

+1 same problem here with Meteor

There is nothing actionable here from uglify's perspective. If some code depends on certain symbols not being mangled, it's up to the user to either exclude those symbols from being mangled or disabling mangle altogether.

+1 using webpack to build bundle. Disable mangle helps, but increase bundle size up to 50%

+1

This is angular 2 problem, and it was fixed before rc 5, but rc 5 something breaks again. Issue present in angular/angular repo.

you can close this issue

Angular appears to have fixed this problem:

https://github.com/angular/angular/issues/10618#issuecomment-243532360

This issue can be closed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lhtdesignde picture lhtdesignde  路  3Comments

Jimbly picture Jimbly  路  4Comments

alexlamsl picture alexlamsl  路  4Comments

pvdz picture pvdz  路  3Comments

kzc picture kzc  路  5Comments