Skip to content

optimization: true breaks transitiveCompileScopes for jit compiled components #37216

Closed

Description

@AndreyAusianik

🐞 bug report

Affected Package

I believe This issue is caused by @angular/compiler

Is this a regression?

This was working on Angular version 6.1.10

Description

I am trying to compile a simple component

  <form>
        <input type="text"
           name="value"
        [(ngModel)]="value"> {{value}}
  </form>

with some logic


ngOnInit() { setInterval(() => this.value++, 1000);

inside of a module


{
   imports: [CommonModule, FormsModule],
   declarations: [RuntimeComponent],
}

on the client with JIT compiler with the help of compiler.compileModuleAndAllComponentsSync.

with optimization: false set in angular.json everything works perfectly fine: both input and interpolation are updated with every tick of setInterval and typing inside of an input also updates the value.

with optimization: true ngModel directive is just lost. i.e. interpolated value updates every second, but input neither shows values nor causes updates when you type in it.

I've tried to compare the compiled components and modules in optimized vs unoptimized code and here are the differenced that I've found:

  • RuntimeComponentModule.ɵmod.transitiveCompileScopes.compilation contains only my RuntimeComponent in optimized version versus 33 directives and 13 pipes in unoptimized one.
  • RuntimeComponent.ɵcmp.tView contains significantly more items in unoptimized version

In our app far more complex components are compiled, this is just a simpler example with a reproducable issue. At the moment any workarounds would be useful.

Note: you can notice


import { publishFacade } from '@angular/compiler';
publishFacade(window);

in the code. Without this piece compiler is eliminated from the bundle, but this bothers us much less as it could be workarounded

🔬 Minimal Reproduction

it is impossible to reproduce on a stackblitz.
Here is a repository with minimal reproduction example described above
https://github.com/AndreyAusianik/ivy-dynamic-problem/

🔥 Exception or Error

error can be generated if you add something specific to directives to the template. for example
#model="ngModel" then it won't be rendered at all, as the compiler finds nothing exported as ngModel.

🌍 Your Environment

Angular Version:


Angular CLI: 9.1.5
Node: 13.1.0
OS: win32 x64

Angular: 9.1.6
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.901.5
@angular-devkit/build-angular     0.901.5
@angular-devkit/build-optimizer   0.901.5
@angular-devkit/build-webpack     0.901.5
@angular-devkit/core              9.1.5
@angular-devkit/schematics        9.1.5
@angular/cli                      9.1.5
@ngtools/webpack                  9.1.5
@schematics/angular               9.1.5
@schematics/update                0.901.5
rxjs                              6.5.5
typescript                        3.8.3
webpack                           4.42.0

Activity

  1. added this to the needsTriage milestone on May 20, 2020
  2. JoostK commented on May 20, 2020

    @JoostK
    Member

    I believe this shares the root cause of #36255.

  3. AndreyAusianik commented on May 20, 2020

    @AndreyAusianik
    Author

    I believe this shares the root cause of #36255.

    it could, but It compiles with factory of a component in my case for both optimized and unoptimized cases. just the imported scope is lost

  4. AndreyAusianik commented on May 22, 2020

    @AndreyAusianik
    Author

    I tried to override the optimization applied by default, and found that my example works fine with
    compress: { side_effects: false }. hope it helps in investigation.

  5. delagen commented on May 25, 2020

    @delagen
    Contributor

    Seems Terser drop moduleMetadata defined by ngtsc marked as pure

    var SomeModule= /** @class */ (function() {
    	function SomeModule() {
    	}
    
    	SomeModule.ɵmod = ɵngcc0.ɵɵdefineNgModule({type: SomeModule});
    	SomeModule.ɵinj = ɵngcc0.ɵɵdefineInjector({
    																  factory: function SomeModule_Factory(t) {
    																	  return new (t || SomeModule)();
    																  }
    															  });
    	(function() {
    		(typeof ngJitMode === "undefined" || ngJitMode) && ɵngcc0.ɵɵsetNgModuleScope(SomeModule, {
    			declarations: function() {
    				return [...];
    			}, exports: function() {
    				return [...];
    			}
    		});
    	})();
    	/*@__PURE__*/
    	(function() {
    		ɵngcc0.ɵsetClassMetadata(SomeModule, [
    			{
    				type: NgModule,
    				args: [
    					{
    						declarations: [
    							...
    						],
    						exports: [
    							...
    						]
    					}
    				]
    			}
    		], function() {
    			return [];
    		}, null);
    	})();
    	return SomeModule;
    }());
    export {SomeModule};
    

    become after Terser with side_effects

    var SomeModule= /** @class */ (function() {
    	function SomeModule() {
    	}
    
    	SomeModule.ɵmod = ɵngcc0.ɵɵdefineNgModule({type: SomeModule});
    	SomeModule.ɵinj = ɵngcc0.ɵɵdefineInjector({
    																  factory: function SomeModule_Factory(t) {
    																	  return new (t || SomeModule)();
    																  }
    															  });
    		return SomeModule;
    }());
    export {SomeModule};
    
  6. alxhub commented on May 26, 2020

    @alxhub
    Member

    Thanks @AndreyAusianik for the issue report! This is indeed a problematic use-case.

    The issue here is that the CLI supports two compilation "modes":

    • No optimizations (allows JIT/AOT interop)
    • Full optimizations (AOT-only)

    The missing mode here would be a "JIT-compatible optimizations" which still run most of the Terser pass on your code, but allows ngJitMode-guarded expressions to survive. This is not a use case which our tooling currently supports.

  7. AndreyAusianik commented on May 28, 2020

    @AndreyAusianik
    Author

    Thanks @alxhub !
    is JIT-compatible omptimization mode planned to be implemented?

  8. modified the milestones: needsTriage, Backlog on Sep 22, 2020
  9. volante007 commented on Oct 15, 2020

    @volante007

    I tried to override the optimization applied by default, and found that my example works fine with
    compress: { side_effects: false }. hope it helps in investigation.

    Hi @AndreyAusianik, which file & config did you modify to get this to work?

  10. alan-agius4 commented on Nov 10, 2020

    @alan-agius4
    Contributor

    Hi all,

    AOT+JIT interop with any build optimizations is not a supported path and we don't recommend this approach to be used for production. The main reasons are that this approach increases drastically the bundle sizes, performance overhead and exposes your application to cross-site scripting (XSS) since compiling components at runtime is a XSS vulnerability in itself.

    I created an issue to capture and document this: #39624

  11. angular-automatic-lock-bot commented on Dec 11, 2020

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  12. locked and limited conversation to collaborators on Dec 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions