Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
lacolaco opened this issue Mar 27, 2017 · 99 comments
Closed

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

lacolaco opened this issue Mar 27, 2017 · 99 comments
Labels
area: compiler Issues related to `ngc`, Angular's template compiler freq1: low P5 The team acknowledges the request but does not plan to address it, it remains open for discussion type: use-case
Milestone

Comments

@lacolaco
Copy link
Contributor

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

@Toxicable
Copy link

Toxicable commented Mar 27, 2017

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

@patrikx3
Copy link

Same error for me.

@Paladinium
Copy link

Any workaround?

@patrikx3
Copy link

patrikx3 commented Apr 15, 2017

use JIT only and wait until AOT is workling together at once... :) If you need JIT with AOT together anyway you will have larger bundle so it is possible same time any way...
AOT + JIT = minus speed, plus bundle size
JIT = minus speed, plus bundle size

You will be the same any way.
:)

@Paladinium
Copy link

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},
    ],
    ...
})

@patrikx3
Copy link

@Paladinium are you able to create dynamic component with JIT? Right now I still get a No NgModule metadata TemplateModule (which I created on the fly), can you do it???

@Paladinium
Copy link

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

@patrikx3
Copy link

@Paladinium Sorry, my error, are you able to compile with AOT? So AOT + JIT?

@Paladinium
Copy link

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.

@patrikx3
Copy link

@Paladinium Do you have an example? I use the same code, but it shows No NgModule metadata TemplateModule :(

@Paladinium
Copy link

Paladinium commented Apr 18, 2017

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>

@maxbellec
Copy link

maxbellec commented Apr 18, 2017

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

@patrikx3
Copy link

I get the same error No NgModule metadata found for RuntimeComponentModule, do you have a full example? The GenericUiComponent, GenericTypeCreator app and the module?
I am thinking just some import or something setting from the module or the app. Please help me out.
I get always the same error.

Looks like the component is decorated, but the module is missing something somewhere. I get the same code.

@patrikx3
Copy link

Are you guys using 4.0.1 or 4.0.2? Is it the same for you?

What is you app and module settings? :)

@maxbellec
Copy link

On 4.0.1. Below are my module settings. To generate components, my code is similar to that of @Paladinium, and derived from http://stackoverflow.com/a/38888009/3218806

export function cookieStrategy() {
  console.log("COOKIE STRATEGY");
  return  new CookieXSRFStrategy('csrftoken', 'X-CSRFToken');
}

// https://github.com/angular/angular/issues/15510#issuecomment-294860905
export function createJitCompiler () {
  return new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
}


@NgModule({
  declarations: [
    ... // components and pipes
  ],
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot(routes),
    BrowserAnimationsModule,
    HttpModule,
    SimpleNotificationsModule.forRoot(),
  ],
  bootstrap:    [AppComponent],
  providers: [
    // from http://plnkr.co/edit/wh4VJG?p=preview
    SvgPageTemplateBuilder,
    SvgPageComponentBuilder,
      ... // services
    {
      provide: APP_BASE_HREF,
      useValue: '/',
    },
    { provide: Compiler, useFactory:  createJitCompiler},
    // this is useful for dates and currencies
    { provide: LOCALE_ID, useValue: "fr-FR" },
    // CSRF protection
    { provide: XSRFStrategy, useFactory: cookieStrategy },
    ],
})
export class AppModule {}

@patrikx3
Copy link

end this is aot right? because same code component generator for me, all same, and my bundle is bigger with aot as well, but it is giving same error. jit working with aot not, so weird.

are you sure you using aot???

@Paladinium
Copy link

@maxbellec , yes, right, this is where I got the original code from. @patrikx3 : yes, we're using AOT. It seems to be that you should open a question on stackoverflow since it has nothing to do with this issue.

@maxbellec
Copy link

My bad, actually my code compiles with AOT but I still get the No NgModule metadata TemplateModule when the dynamic component should get compiled (the rest of the site works fine), so @Paladinium 's solution is not (yet) working for me.
I'll try digging into it, @Paladinium if you happen to have made the same mistake as I did (your app is compiling and working but you can't use Jit to compile components), I'd be interested.

@patrikx3
Copy link

you must be using JIT, instead AOT. for sure!!!!
you cannot create a dynamic module. for sure. the compiler does not allow dynamic modules! and you can decorate a component, but you cannot a dynamic module.

i tested all, the same code in 2.3 (angular-cli), 4.0, 4.0.1, 4.0.2, i understand the compiler how to create dynamic components and dynamic modules and even the angular guys say that you cannot create a dynamic module, so you are using JIT!!! for sure!

@patrikx3
Copy link

i am a pro in compiling in angular by now.. by hand...
i am using AOT my self, love it. (bigger bundle yes, so i turned on gzip in nginx, so instead of 2.75MB is just 600KB ) but compiling is much faster like 2 seconds in a small project.

works even with 2G fast, max 15 seconds, from above 3G is fast!

i had to turn off JIT because it was not doing anything and solved in a different way.

@Paladinium
Copy link

@maxbellec : no, I never had this problem. I recommend opening a stackoverflow to not spam this bugreport with an application specific issue. Sounds to me like TemplateModule is not annotated with @NgModule. Or maybe check your tsconfig file that you need for AOT. For example the compiler options:

...
"compilerOptions": {
    "target": "es5",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "noEmitHelpers": true,
    "pretty": false,
    "sourceMap": true,
    "lib": ["es2015", "dom"],
    "noEmit": true,
    "strictNullChecks": false,
    "skipDefaultLibCheck": true,
    "noUnusedParameters": false,
    "noUnusedLocals": false,
    "typeRoots": [
      "node_modules/@types"
    ]
  }
...

@patrikx3
Copy link

patrikx3 commented Apr 19, 2017

your build is JIT, you just think it is AOT.
even the angular guys say you cannot create modules on the fly with AOT and everyone says No NgModule metadata

you are the one with AOT creating modules on the fly

@maxbellec
Copy link

maxbellec commented Apr 19, 2017

@Paladinium given the many discussions and workarounds found, I also doubt (but I hope you're right) you are actually using AOT AND making JITCompiler work.

Many well informed people (including contributors) seem to suggest it is not possible. Can you triple check everything works for you? If yes, could you provide a working plunker? It would make many people's life easier.

#15343
#11780
http://stackoverflow.com/a/42619501/3218806

@patrikx3
Copy link

patrikx3 commented Apr 19, 2017

@maxbellec : @Paladinium 's build is 100% is JIT!!! I tested all! v2, v4 , using angular-cli, my own , and always same error. how come angular-cli is wrong same error??? :)
besides, his tsconfig.json is not the lowest js settings possible. :)

@Paladinium it is a miracle for you! i wish i get it as well!! :) :) :) 👍

@Paladinium
Copy link

@maxbellec : I can provide you a ZIP that you can try ourself (see attachment). To run:

  • If you want to run it in dev mode (which uses JIT all the time): npm run server:dev (if you clear the 'output' folder, you have to re-created the DLLs by running npm run build:dll)
  • If you want to run it in prod mode (which uses AOT): npm run server:prod

I suggest you start with looking at the app.component.html which includes the same component twice: once 'normally' and once using the dynamic-ui component which gets a string (here with a component called 'some-component', but it could also be just HTML or both) that is then compiled using JIT and inserted into the page.

Just to not get confused: yes, 'some-component' is pre-compiled by AOT. But the string passed to the dynamic-ui component needs to be compiled and interpreted by Angular - which is the step where the JIT compiler is needed.

One more thing: I never said it can be done using the angular CLI. We created our own build based on Webpack 2 and I'm not saying that it's perfect. However, it manages the complexity of switching between JIT and AOT for dev and prod mode. Also note that among others, the entry points are different: main.browser.ts and main.browser.aot.ts.

I'm sure there is much to be improved in the given example, but for sure: it shows how to dynamically create a UI using JIT within an AOT compiled application. Let me know what you think.

angular4-aot-jit.zip

@pkozlowski-opensource pkozlowski-opensource added area: compiler Issues related to `ngc`, Angular's template compiler and removed area: core Issues related to the framework runtime labels Mar 11, 2020
@ngbot ngbot bot modified the milestones: Backlog, needsTriage May 28, 2020
@alxhub
Copy link
Member

alxhub commented May 28, 2020

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.

@maxtacco
Copy link

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.

@mehtadhaval
Copy link

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 ?

@ngbot ngbot bot modified the milestones: needsTriage, Backlog Sep 22, 2020
@jelbourn jelbourn added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent P5 The team acknowledges the request but does not plan to address it, it remains open for discussion and removed P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent severity3: broken labels Oct 1, 2020
@alan-agius4
Copy link
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

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area: compiler Issues related to `ngc`, Angular's template compiler freq1: low P5 The team acknowledges the request but does not plan to address it, it remains open for discussion type: use-case
Projects
None yet
Development

No branches or pull requests