angular / angular

Deliver web apps with confidence 🚀
https://angular.dev
MIT License
95.1k stars 24.9k forks source link

[RC5]: Minified bundle breaks #10618

Closed antonybudianto closed 7 years ago

antonybudianto commented 7 years ago

I'm submitting a ... (check one with "x")

[x] bug report
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior Minified bundle breaks, but SOLVED temporarily by either:

Error traces:

lib-2cf12bf509.js:7 Unhandled Promise rejection: Template parse errors:
Can't bind to 'brand' since it isn't a known property of 'as-navbar'.
1. If 'as-navbar' is an Angular component and it has 'brand' input, then verify that it is part of this module.
2. If 'as-navbar' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message.
 ("<as-navbar [ERROR ->][brand]="appBrand"></as-navbar>
<div class="container" style="margin-top: 100px;">
    <router-outlet></"): a@0:11 ; Zone: <root> ; Task: Promise.then ; Value:

Expected/desired behavior The minified bundle should work as in RC4

Reproduction of the problem https://github.com/OasisDigital/rc5-declaration-order

npm install

// try run in dev, it works well now
npm start

// try run in prod, the bundle created but break
npm run serve-build

// now try changing the mangle option and retry, it works!

What is the expected behavior? Should work as in RC4

What is the motivation / use case for changing the behavior?

Please tell us about your environment:

kylecordes commented 7 years ago

"ngc" is the command line Angular 2 template precompiler. It is in the package "npm i @angular/compiler-cli".

TheLarkInn commented 7 years ago

@kylecordes thank you

vthinkxie commented 7 years ago

@ofuangka just set mangle:false will be ok

spock123 commented 7 years ago

Fwiw, it works perfectly in Webpack2 with the mangle options set as described in this thread.

brendanalexdr commented 7 years ago

It has been suggested that the solution to this problem is to set mangle=false as follows:

return builder.buildStatic('build/src/js/main.js', 'build/app.js', {minify: true, mangle: false});

I am not seeing where or how to make this change. Can anyone advise?

thelgevold commented 7 years ago

@nukuuk I have a sample with systemjs builder. I have not confirmed that this fixes it, but you can try it here: https://github.com/thelgevold/angular-2-samples/blob/master/gulpfile.js

thelgevold commented 7 years ago

It works to set all properties to false, but I have not tried minify = true and mangle = false

brendanalexdr commented 7 years ago

@thelgevold I see, you are referring to using systemjs builder with gulp to do bundle/minify etc. Yes I was using this until I recently started using the angular-cli with RC5. I had to set mangle=false to make the builds work. But what is the point of angular-cli with RC5 if we have to fall back on gulp/systemjs builder to bundle?

cdarken commented 7 years ago

@nukuuk can you describe exactly what you did? where exactly did you used mangle = false ? I'm using angular cli 1.0.0-beta.10

brendanalexdr commented 7 years ago

@cdarken Currently I am using angular-cli 1.0.0-beta.11-webpack.2 with RC5 (the following may not be the case with angular-cli 1.0.0-beta.10, I don't know). And as this thread reports, if you run ng build -prod this will break the app because (presumably)
a) the cli mangles the javascript bundle and angular or webpack doesn't like this, and/or b) there is some problem with the ordering of the the components in main module declaration.

There is not bug fix for this now, hopefully will happen soon.

In the mean time, my workaround involves running ng build which will bundle the app but not minify/mangle the javascript bundles. Then I am using gulp-uglify with {mangle: false} to concatenate and minify into a final production bundle. It works. But I would much prefer if the angular cli finished the job.

msegers commented 7 years ago

Hmm I've tried to re-order my smaller components etc. as first elements in my declarations, no cigar it now starts complaining about angular-material components which are in a separate module.

I disabled my Uglify plugin for now, I'm using a build based on angularclass webpack starter if anyone cares.

mikeeus commented 7 years ago

@msegers in the angularclass-webpack-starter you can enable UglifyJsPlugin in the options set

mangle: { screw_ie8 : true, **keep_fnames: true ** }, //prod

@ofuangka this code is a setting for the UglifyJsPlugin in your production webpack.config.js file. It should be placed inside your plugins array.

plugins: [ new UglifyJsPlugin({ mangle: { screw_ie8 : true, keep_fnames: true } }) ]

herlon214 commented 7 years ago

:+1:

@mikeeus solution worked fine to me! But I needed to move to https://github.com/mishoo/UglifyJS2 (I was using the first version).

Thanks!

ricklove commented 7 years ago

@mikeeus I have the same confusion as @nukuuk with where to put the "mangle" setting - because there is no "webpack.config.js" file (I did a fresh "ng new" with angular-cli 1.0.0-beta.11-webpack.2 - RC5).

So, where do I create the webpack.config.js file and do I need to modify angular-cli.json to use it?

Attached is a screenshot of the files I have with the default angular-cli.json settings.

capture

herlon214 commented 7 years ago

Guys correct me if I'm wrong but I think that you use the mangle option when you are uglifying the project.

@ricklove, I think that you doesn't Have to use mangle, once your project isn't being minifed / uglified

ricklove commented 7 years ago

Solution to fix "ng serve --prod" with "angular-cli 1.0.0-beta.11-webpack.2 - RC5"

Ok, I finally figured out a way by directly changing the file in the node_modules folder, but I imagine there is a better place to do this within my project. Thanks @mikeeus for the setting.

capture2

@herlon214 - It works fine with the normal build, I am trying to do a --prod build which is where I have a problem.

brendanalexdr commented 7 years ago

@rickove right now I am not using angular-cli to build for production (ie not using ng build -prod) because, as we have discussed here, it breaks the app. I can't find anyplace where you can set the minifying options ie mangle=false.

For the time being, i am using angular-cli ONLY in development mode (and that has problems too because webpack doesn't seem to be producing good source maps which is causing havoc setting breakpoints). In the event that I need a production build, i am 1) doing a development build (ng build) then 2) minifying and concatenating the results with gulp-uglify with the mangle option set to false return gulp.src(files) .pipe(concat("main.bundle.min.js")) .pipe(uglify({ mangle: false })) .pipe(gulp.dest("./some/location/js"));

Am looking forward to the next build where this is fixed.

UPDATE: I didn't see @ricklove's solution above editing the webpack config when posting this. His workaround works, ignore this.

ricklove commented 7 years ago

@nukuuk, see my last post, it seems to be working for me for the prod build, at least it does reduce the file size to a large degree

herlon214 commented 7 years ago

There is no fix to keep "keep_fnames" disabled? :( it increased my build file in almost 400kb

TheLarkInn commented 7 years ago

This is being worked on by the ng team to fix. This is a workaround so that your builds will still work.

kylecordes commented 7 years ago

@herlon214 Most likely this is primarily a temporary workaround. I got the impression early in the thread that there is a deeper solution possible that will make it not necessary anymore. Also, apparently it is already not necessary if you use the template precompiler... but doing so is rather tricky at the moment, there is very little out there yet in terms of working examples, support by CLI is on the way but not here, etc.

So for the moment, the obvious thing is to just wave your hands and pretend the extra 400KB is not there, because it is temporary. :-)

brendanalexdr commented 7 years ago

@ricklove you are a genius! It works, my production builds are now slim and trim. Many thanks!

herlon214 commented 7 years ago

Thanks for the explanation @TheLarkInn , @kylecordes :smile:

ricklove commented 7 years ago

@nukuuk If 1MB counts as slim and trim :) To be fair with gzip it takes mine down to about 250kb, which is actually not bad for a SPA. It sure beats 3MB that's for sure and I can live with these results for now.

herlon214 commented 7 years ago

Guys, any of you had a problem with gzip ? I'm a bit afraid to use gzip and some of my clients use a browser that doesn't accept

2016-08-19 12:25 GMT-04:00 Rick Love notifications@github.com:

@nukuuk https://github.com/nukuuk If 1MB counts as slim and trim :) To be fair with gzip it takes mine down to about 250kb, which is actually not bad for a SPA. It sure beats 3MB that's for sure and I can live with these results for now.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/angular/angular/issues/10618#issuecomment-241064821, or mute the thread https://github.com/notifications/unsubscribe-auth/ADQtMWZkEuTQR3dzArWU2oMOKVASRu7Yks5qhdjegaJpZM4JgpvN .

Att, Herlon Aguiar

REPTILEHAUS commented 7 years ago

Hey Guys.. anyone thats not using gulp minify and is using system js builder try this in your gulp config file.. worked for me:

    builder: {
        normalize: true,
        minify: true,
       // mangle: true, 
        mangle:false,
        runtime: false,
        globalDefs: { DEBUG: false, ENV: 'production' }
    }
};
aconfee commented 7 years ago

I am seeing this as well using Webpack and the 'new webpack.optimize.UglifyJsPlugin()' plugin. As @antonybudianto suggested, ordering my module declarations topologically is a valid workaround for now.

Excited to see this one fixed!

kjvelarde commented 7 years ago

I tried rearranging the declarations but that didn't work for me. Adding moduleId: module.id in most of my components however work like magic. kudos to you sir.

Looking forward to the fix.

CaptainCodeman commented 7 years ago

How does something like this get broken and still released in RC5?

Bundling and minification are not new things, they are an essential and fundamental part of the JavaScript ecosystem. The bundling and minification within Angular 2 has had plenty of issues before - more excusable when it was Alpha or Beta and maybe the first couple of times it happened.

Surely the testing and release system should include some tests that include some bundling and minification?

aconfee commented 7 years ago

To @CaptainCodeman 's point, it is concerning that this bug made it this far. I of course understand that regressions are inevitable, especially when working on massive scale projects that are expected to work with any number of unknown plugins and addons from the open source world. Kudos to the Angular team all around.

But that being said, will there be any officially supported compilers, bundlers, and minifiers (etc) like SystemJS and Webpack that will have a large suite of tests and dev effort focused on them? Would anyone on the Angular team be able to add a list of strongly supported, compatible tooling in their documentation? The idea being that if a tool is listed in the docs, it is expected to work, otherwise, use at your own risk (such is open source).

Thanks, Adam

ShankarSumanth commented 7 years ago

Hi Guys,

Just to add a small note here, I have disabled the mangle option and the whole UglifyJsPlugin(). But it still breaks the routers.

For example, If I want to navigate to localhost:3000/company and if I have a default redirect like {path: '', redirectTo: '/company', pathMatch:'full'} then this works, but If i try to navigate directly to localhost:3000/company url, then i see a complete blank page( "Get /company" Error (404): "Not found" ). Nor any other route can be loaded (the child routes from /company works, but routes at same level). I will give a try for the AOT solution and post my comment here later.

GaryB432 commented 7 years ago

@ShankarSumanth I think you may need the HashLocationStrategy in your module's provider

ahuvafischer commented 7 years ago

i tried the suggestions in the thread. still getting:

`Can't bind to 'icon' since it isn't a known property of 'button'. ("ver':hovered,'ui-state-focus':focused,'ui-state-disabled':disabled}"

<button type="button" [ERROR ->][icon]="icon" pButton ngIf="showIcon" (click)="onButtonClick($event,in)" [ngClass]="): Calendar@7:31 ; Zone: ; Task: Promise.then ; Value: BaseException {message: "Template parse errors:↵Can't bind to 'icon' since …in)" ↵ [ngClass]="): Calendar@7:31", stack: "Error: Template parse errors:↵Can't bind to 'icon'…odules/zone.js/dist/zone.js?1472035648186:365:38)"} Error: Template parse errors: Can't bind to 'icon' since it isn't a known property of 'button'. ("ver':hovered,'ui-state-focus':focused,'ui-state-disabled':disabled}" <button type="button" [ERROR ->][icon]="icon" pButton ngIf="showIcon" (click)="onButtonClick($event,in)" [ngClass]="): Calendar@7:31 at new BaseException (http://localhost:5555/node_modules/@angular/compiler/bundles/compiler.umd.js:5116:27) at TemplateParser.parse (http://localhost:5555/node_modules/@angular/compiler/bundles/compiler.umd.js:8318:23) at RuntimeCompiler._compileTemplate (http://localhost:5555/node_modules/@angular/compiler/bundles/compiler.umd.js:15941:55) at eval (http://localhost:5555/node_modules/@angular/compiler/bundles/compiler.umd.js:15869:87) at Set.forEach (native) at compile (http://localhost:5555/node_modules/@angular/compiler/bundles/compiler.umd.js:15869:51) at ZoneDelegate.invoke (http://localhost:5555/node_modules/zone.js/dist/zone.js?1472035648186:332:29) at Zone.run (http://localhost:5555/node_modules/zone.js/dist/zone.js?1472035648186:225:44) at http://localhost:5555/node_modules/zone.js/dist/zone.js?1472035648186:591:58 at ZoneDelegate.invokeTask (http://localhost:5555/node_modules/zone.js/dist/zone.js?1472035648186:365:38)consoleError @ zone.js?1472035648186:484_loop_1 @ zone.js?1472035648186:511drainMicroTaskQueue @ zone.js?1472035648186:515ZoneTask.invoke @ zone.js?1472035648186:437`

bahodirk commented 7 years ago

@ahuvafischer I had the same issue, and only solution worked for me was that I had to list all components in @NgModule in app.module. Whenever I add another modules B and C, then import B and C to my app.module it breaks.

ahuvafischer commented 7 years ago

@bahodirk how about third party modules?

bahodirk commented 7 years ago

@ahuvafischer I am still coping them to dist deployment folder. In index.html: <script src="../node_modules/moment/min/moment-with-locales.min.js"></script>

mirkonasato commented 7 years ago

With ES5 if declarations are in the "wrong" order some components will simply not be displayed, without any errors. Plunk: plnkr.co/edit/7qi6Um1UzTXBDrV5q1Js

If this is not fixed for 2.0.0 it would be better to say that Angular 2 does not support ES5, period.

frankcarey commented 7 years ago

I don't think that I'm minifying the bundle, but I've just upgraded a small project to RC5 and am getting the same errors. I updated node_modules/@angular/compiler/src/url_resolver.js like so and it fixes the immediate error for me..

 function _split(uri) {
+    uri += '';
     return uri.match(_splitRe);
 }

The error was that at one point, uri == 114 and wasn't a string. When I added the above code, it fixed it so far.

frankcarey commented 7 years ago

OK, now that I'm using uglify with mangle: {screw_ie8 : true, keep_fnames: true } set, I'm not sure I need to patch node_modules/@angular/compiler/src/url_resolver.js anymore..

UPDATE:

Nope, I still need it because what's being passed in as the moduleId is still an integer 114 and not a string. var scheme = url_resolver_1.getUrlScheme(moduleId);

IgorMinar commented 7 years ago

the Function#name minification issue was resolved by 51877ef4ed5016bd0c57f35e7bbb1ee26511ab5d, once rc.6 is out it should not longer be necessary to tell uglify not to mangle function names.

ishehata commented 7 years ago

i am still having the same issue with angular-2.rc6 with webpack2, when i used mangle: false; in my webpack configuration the error changed from t.match is not a function to uri.match is not a function i have opened an issue in angular2-webpack-starter https://github.com/AngularClass/angular2-webpack-starter/issues/958

qdouble commented 7 years ago

@eslammostafa That's not a minify issue, that's related to this: https://github.com/angular/angular/issues/10626. Basically, you could either remove any module.id references from the core or third party libraries, or use a string-replace-loader preloader in webpack that strips it, or use the NamedModulesPlugin if you are using the latest version of webpack 2 or manually put .toString() in the part that causes the error in the node_modules folder.

iBasit commented 7 years ago

@ricklove Thank you for suggestion, that worked out perfectly for me.

For others:

nano node_modules/angular-cli/addon/ng2/models/webpack-build-production.ts 

edit mangle: { screw_ie8 : true }, //prod to mangle: { screw_ie8 : true, keep_fnames:true }, //prod

ng build -prod

Enjoy!

rperfect commented 7 years ago

@ricklove I've upgraded to the latest releases Angular 2.0.0 and Angular CLI 1.0.0-beta.14 but the edit mangle approach is not working.

The webpack-build-production.ts file is in a different location. It now seems to be in;

node_modules/angular-cli/models/webpack-build-production.js

When I edit this file and change it to include keep_fnames:true I still get the error...

`Can't bind to 'customerNumber' since it isn't a known property of 'channel-detail-panel'.

  1. If 'channel-detail-panel' is an Angular component and it has 'customerNumber' input, then verify that it is part of this module.
  2. If 'channel-detail-panel' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message. ("nnel-detail-panel *ngFor="let channel of channelList.results" [ERROR ->][customerNumber]="customerNumber" [channelModel]="channel"> "): ChannelManagementComponent@10:38`

I've compared the two main.xxxx.bundle.js files with and without this workaround and they are different. The one with the workaround is about 1.47 Mb and one without the change is about 1.32 Mb.

I see this issue has been closed and "included" in release 2.0.1 of Angular but I can't tell what was actually changed.

I've also tried a number of different combinations with the declarations order but I still get the problem.

Which versions of Angular and Angular CLI are people using in which the edit mangle approach works??

Any help would be much appreciated :-)

[Update] - I've just seen @IgorMinar comment about 51877ef fixing this issue in rc.6 but I'm using release 2.0.0 and it still seems to be a problem.

[SOLVED] - Ack, my fault. I think my actual problem was that my selector declaration in the sub-component "channel-detail-panel" wasn't right it was set to "app-channel-detail-panel" when it should have been just "channel-detail-panel" - so the error was right, but not explaining things very well.

I think this issue is working correctly now in Angular 2.0.0 + Angular CLI 1.0.0-beta.14 without the edit mangle workaround. Just make sure your selector declarations are correct.

mchamo commented 7 years ago

Seeing same issue. I'm using the webpack intro that is on the angular.io. It works fine when built in dev mode, however, when built in prod mode, the uglify process seems to break it. I tried shuffling the order of declarations in my app module as was suggested above, and now I am seeing errors related to built Angular components (see below), so shuffling order of declarations does not address this.

I am perplexed by these kind of errors, and would hope that the Angular team would be mindful of these kinds of changes, since one can easily waste several hours on these types of errors. Any suggestions are appreciated. Thanks!

Can't bind to 'routerlink' since it isn't a known property of 'a'. ("ab of item.Tabs" style="background-color:#d3d3d3;border:1px solid #fff;padding:5px 5px 5px 5px"> <a [ERROR ->][routerlink]=tab.Link ngif="tab.IsRoute===true">{{tab.Text}} <a (click)=navigate(tab.Link) ngif"): AppNav@0:193 Can't bind to 'ngif' since it isn't a known property of 'a'.

rperfect commented 7 years ago

@mchamo Isn't your ngIf spelt wrong? It's not ngif - it's *ngIf with a capital I.

mchamo commented 7 years ago

Hi @rperfect, as I described in my post above, everything works fine when running the app build process in dev mode. Also, my original error message when running in prod build mode was identical to the errors in the beginning of this thread. After shuffling the order of declarations, including shifting AppComponent declaration, as suggested by some of the other posts in this thread, I started seeing the ngIf issue. By going back to my original declaration list, I'm back to original error message. The root cause of the issue seems to be the uglify process with web pack build. Switching back to non-uglified version, i.e, dev build, everything works fine. However, size of page is about 2 MB, hence the desire to minify.

princemaple commented 7 years ago

@mchamo you probably have htmlLoader, make sure you set it like so:

      {
        test: /\.html$/,
        loader: 'html',
        options: {
          minimize: true,
          removeAttributeQuotes: false,
          caseSensitive: true, // <- this
          customAttrSurround: [
            [/#/, /(?:)/],
            [/\*/, /(?:)/],
            [/\[?\(?/, /(?:)/]
          ],
          customAttrAssign: [/\)?\]?=/]
        }
      },
mchamo commented 7 years ago

@princemaple thanks for the suggestion, still getting the original error message (see below) when running after building for prod mode. Building in dev mode, works fine.

zone.js?fad3:355Unhandled Promise rejection: Template parse errors: Can't bind to 'appname' since it isn't a known property of 'app-nav'.

  1. If 'app-nav' is an Angular component and it has 'appname' input, then verify that it is part of this module.
  2. If 'app-nav' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message.
Martin-Luft commented 7 years ago

@mchamo the only thing you need is:

new webpack.optimize.UglifyJsPlugin({
  mangle: {
    keep_fnames: true
  },
})