Skip to content

[4.0.0] bug(compiler-cli): Cannot use JitCompiler with AoT #15510

Closed

Description

@lacolaco

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

[x] bug report => search github for a similar issue or PR before submitting
[ ] 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
Since 4.0.0, JitCompiler cannot be used with AoT compilation. It's exported from @angular/compiler but its compiled import path is @angular/compiler/src/jit/compiler

app.module.ts

import { NgModule, Compiler } from '@angular/core';
import { JitCompiler } from '@angular/compiler';

import { AppComponent } from './app.component';

@NgModule({
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    providers: [
        {
            provide: Compiler, useExisting: JitCompiler,
        },
    ]
})
export class AppModule {
}

app.module.ngfactory.ts(summary)

import * as import0 from '@angular/core';
import * as import1 from './app.module';
import * as import2 from './app.component.ngfactory';
import * as import3 from '@angular/compiler/src/jit/compiler';
class AppModuleInjector extends import0.ɵNgModuleInjector<import1.AppModule> {
  _AppModule_0:import1.AppModule;
  __Compiler_1:any;
  constructor(parent:import0.Injector) {
    super(parent,[import2.AppComponentNgFactory],[import2.AppComponentNgFactory]);
  }
  get _Compiler_1():any {
    if ((this.__Compiler_1 == null)) { (this.__Compiler_1 = this.parent.get(import3.JitCompiler)); }
    return this.__Compiler_1;
  }

@angular/compiler/src/jit/compiler is not existing JS file (because of 4.0.0 FESM). webpack bundling crashes by this problem.

Expected behavior

Resolve JitCompiler from @angular/compiler

Minimal reproduction of the problem with instructions

https://github.com/laco0416/ngrepro-0001

  1. git clone
  2. npm install
  3. npm run ngc
  4. see the generated files

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

Please tell us about your environment:
OSX

  • Angular version: 4.0.0
  • Language: TypeScript 2.2

  • Node (for AoT issues): node --version = 6.9.5

Activity

  1. Toxicable commented on Mar 27, 2017

    @Toxicable

    Just to add to this, you can reproduce it with the CLI aswell.
    ng new myproj
    cd myproj
    add to app.module.ts

      providers: [ { provide: Compiler, useExisting: JitCompiler, }, ],
    

    run ng serve --aot
    ng serve works fine however
    and you should get an error similar to Error: Can't resolve '@angular/compiler/src/jit/compiler' in '...\myproj\src\$$_gendir\app' which indicates the same deep import as @laco0416 pointed out in the ngc build

  2. Paladinium commented on Apr 15, 2017

    @Paladinium

    Any workaround?

  3. Paladinium commented on Apr 15, 2017

    @Paladinium

    Here's the workaround using the JitCompilerFactory:

    import {JitCompilerFactory} from '@angular/compiler';
    ...
    // Need an exported function to make it work with AOT:
    export function createJitCompiler () {
        return new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
    }
    ...
    @NgModule({
        providers: [
           { provide: Compiler, useFactory:  createJitCompiler},
        ],
        ...
    })
    
  4. Paladinium commented on Apr 18, 2017

    @Paladinium

    Yes, I can, but the code to create a module dynamically is complex. It's along the lines of this: http://www.themonkeythemes.com/blog_backoffice/create-components-at-runtime-with-angular

  5. Paladinium commented on Apr 18, 2017

    @Paladinium

    Yes. We use AOT. Except for one part of the UI that we wanted to build the UI dynamically (based on a server side configuration) which is where we use JIT.

  6. Paladinium commented on Apr 18, 2017

    @Paladinium

    This is the code that creates a factory for creating a component:

    @Injectable
    export class GenericTypeCreator {
        private _cacheOfFactories: {[templateKey: string]: ComponentFactory<GenericContextData>} = {};
    
        constructor(protected compiler: Compiler) {} // This is the JIT compiler
    
        createComponentFactory(template: string): Promise<ComponentFactory<GenericContextData>> {
    
            let factory = this._cacheOfFactories[template];
    
            if (factory) {
                console.log('Module and Type are returned from cache');
    
                return new Promise((resolve) => {
                    resolve(factory);
                });
            }
    
            // unknown template ... let's create a Type for it
            let type   = this.createNewComponent(template);
            let module = this.createComponentModule(type);
    
            return new Promise((resolve) => {
                this.compiler
                    .compileModuleAndAllComponentsAsync(module)
                    .then((moduleWithFactories) => {
                        factory = moduleWithFactories.componentFactories.find(component => component.componentType === type);
                        this._cacheOfFactories[template] = factory;
                        resolve(factory);
                    })
                    .catch(error => {
                        console.log(error);
                    });
            });
        }
    
        private createNewComponent (tmpl:string) {
            @Component({
                selector: 'dynamic-component',
                template: tmpl,
            })
            class CustomDynamicComponent implements GenericContextData {
                 // In our case, this component gets a context set. But you can do with that component whatever you want.
          }
          return CustomDynamicComponent;
      }
    
        private createComponentModule (componentType: any) {
            @NgModule({
                imports: [
                    // whatever modules you need as a dependency,
                    CommonModule,
                    ReactiveFormsModule
                ],
                declarations: [
                    componentType
                ]
            })
            class RuntimeComponentModule { }
            // a module for just this Type
            return RuntimeComponentModule;
        }
    
    }
    

    And here's the caller:

    @Component({
        selector: 'generic-ui',
        template: '<div #genericUiContentPlaceHolder></div>',
        providers: [SchemaToUiService, GenericDataService]
    })
    export class GenericUiComponent implements ... {
    
        @Input
        templateString: string;
    
        @ViewChild('genericUiContentPlaceHolder', {read: ViewContainerRef})
        public genericUiComponentTarget: ViewContainerRef;
        protected componentRef: ComponentRef<GenericContextData>;
    
        constructor(private genericTypeCreator: GenericTypeCreator) { }
    
       // Note: this component implements a couple of lifecycle methods to trigger the creation of the UI
    
        private buildUi () {
            if (this.componentRef) {
                this.componentRef.destroy();
            }
    
            console.log('The generic template is: ' + this.templateString);
    
            // here we get Factory (just compiled or from cache)
            this.genericTypeCreator
                .createComponentFactory(this.templateString) // <- this is what you pass in to be compiled by JIT
                .then((factory: ComponentFactory<GenericContextData>) => {
                    // Target will instantiate and inject component (we'll keep reference to it)
                    this.componentRef = this.genericUiComponentTarget.createComponent(factory);
                    let component = this.componentRef.instance;
                   // Here we pass data to the component mentioned above
                    component.context = this.ui.context;
                });
        }
    }
    

    And the usage could be:

    <generic-ui [templateString]='whatever string you want to be compiled'></generic-ui>
    
  7. maxbellec commented on Apr 18, 2017

    @maxbellec

    I confirm @Paladinium 's workaround (Angular 4.0.1 angular-cli 1.0.0 and similar code to generate dynamic components). Thanks!

  8. 98 remaining items

  9. alxhub commented on May 28, 2020

    @alxhub
    Member

    Hi all,

    Indeed, JIT and AOT in Angular with the View Engine runtime were independent - it wasn't really possible to mix and match the two. This was a systemic constraint, not a bug in the implementation.

    The good news is that a major design goal for Angular Ivy was to support JIT/AOT interop. In Angular v9+ it's possible to build applications which are AOT compiled but consume dynamic, JIT-compiled components, or even the other way around if desired. There are still a few hiccups (#37216 for example) but overall the support is there.

  10. maxtacco commented on May 28, 2020

    @maxtacco

    Thank you @alxhub. Just like you said Angular9 works fine for us in AOT mode with some JIT dynamic components. The only issue left is devkit/terser optimizations during AOT builds.

  11. mehtadhaval commented on Jul 23, 2020

    @mehtadhaval

    In Angular v9+ it's possible to build applications which are AOT compiled but consume dynamic, JIT-compiled components, or even the other way around if desired.

    @alxhub : I couldn't find any example of this. Can you point me in right direction ?

  12. modified the milestones: needsTriage, Backlog on Sep 22, 2020
  13. added
    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
    P5The team acknowledges the request but does not plan to address it, it remains open for discussion
    and removed
    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
    on Oct 1, 2020
  14. 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

  15. 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.

  16. 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

    P5The team acknowledges the request but does not plan to address it, it remains open for discussionarea: compilerIssues related to `ngc`, Angular's template compilerfreq1: lowtype: use-case

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions