Skip to content

V8.0.0 : loadChildren library module failed : "Error: Runtime compiler is not loaded" on AOT mode #14763

Closed

Description

@robinComa

🐞 bug report

Affected Package

The issue is caused by package @angular/compiler and/or @angular/core and/or @angular/router

Is this a regression?

Yes, the previous version in which this bug was not present was: 7.2.1

Description

I try to load a project library (named shell) as a route "loadChildren", with 2 différents option :

  • loadChildren: () => import('shell').then(m => m.ProfileModule)
  • loadChildren: () => ProfileModule

This 2 ways work correctly on dev mode, but failed on AOT builded mode with an error message. And the route is not loaded.

🔬 Minimal Reproduction

ProfileModule (as an angular library named "shell" on the CLI project) :

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { FlexLayoutModule } from '@angular/flex-layout';

import { ProfileComponent } from './profile/profile.component';
import { TranslateModule } from '../translate/translate.module';
import { GhostModule } from '../ghost/ghost.module';
import { DataModule } from '../data/data.module';
import { ProfileRoutingModule } from './profile-routing.module';
import { ProfileCardComponent } from './profile-card/profile-card.component';

@NgModule({
  declarations: [
    ProfileComponent,
    ProfileCardComponent
  ],
  imports: [
    CommonModule,
    MatCardModule,
    MatListModule,
    MatButtonModule,
    MatIconModule,
    TranslateModule,
    FlexLayoutModule,
    GhostModule,
    DataModule,
    ProfileRoutingModule
  ],
  exports: [
    ProfileCardComponent
  ]
})
export class ProfileModule { }

AppRoutingModule (as an angular project from the same CLI project)

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { SecureGuard } from 'shell';

import { HomeComponent } from './home/home.component';

export function loadProfile() {
  return import('shell').then(m => m.ProfileModule);
}

const routes: Routes = [{
  path: '',
  canActivate: [SecureGuard],
  children: [{
    path: '',
    component: HomeComponent
  }, {
    path: 'profile',
    loadChildren: loadProfile // Here the error at runtime, the error message appear when I go to /profile
  }, {
    path: '**',
    component: HomeComponent
  }]
}];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

🔥 Exception or Error


Uncaught Error: Uncaught (in promise): Error: Runtime compiler is not loaded
Error: Runtime compiler is not loaded
    at e.Jr (main.41ff3398f5c3f189c836.js:1)
    at t.project (main.41ff3398f5c3f189c836.js:1)
    at t._tryNext (main.41ff3398f5c3f189c836.js:1)
    at t._next (main.41ff3398f5c3f189c836.js:1)
    at t.next (main.41ff3398f5c3f189c836.js:1)
    at e._subscribe (main.41ff3398f5c3f189c836.js:1)
    at e._trySubscribe (main.41ff3398f5c3f189c836.js:1)
    at e.subscribe (main.41ff3398f5c3f189c836.js:1)
    at e.call (main.41ff3398f5c3f189c836.js:1)
    at e.subscribe (main.41ff3398f5c3f189c836.js:1)
    at Z (polyfills.06b93379260cc1d8e708.js:1)
    at Z (polyfills.06b93379260cc1d8e708.js:1)
    at polyfills.06b93379260cc1d8e708.js:1
    at e.invokeTask (polyfills.06b93379260cc1d8e708.js:1)
    at Object.onInvokeTask (main.41ff3398f5c3f189c836.js:1)
    at e.invokeTask (polyfills.06b93379260cc1d8e708.js:1)
    at t.runTask (polyfills.06b93379260cc1d8e708.js:1)
    at g (polyfills.06b93379260cc1d8e708.js:1)
    at t.invokeTask [as invoke] (polyfills.06b93379260cc1d8e708.js:1)
    at m (polyfills.06b93379260cc1d8e708.js:1)

🌍 Your Environment

Angular Version:


Angular CLI: 8.0.1
Node: 12.4.0
OS: darwin x64
Angular: 8.0.0
... animations, cdk, common, compiler, compiler-cli, core, forms
... language-service, material, material-moment-adapter
... platform-browser, platform-browser-dynamic, router
... service-worker

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.800.1
@angular-devkit/build-angular      0.800.1
@angular-devkit/build-ng-packagr   0.800.1
@angular-devkit/build-optimizer    0.800.1
@angular-devkit/build-webpack      0.800.1
@angular-devkit/core               8.0.1
@angular-devkit/schematics         8.0.1
@angular/cli                       8.0.1
@angular/flex-layout               8.0.0-beta.26
@angular/pwa                       0.800.1
@ngtools/json-schema               1.1.0
@ngtools/webpack                   8.0.1
@schematics/angular                8.0.1
@schematics/update                 0.800.1
ng-packagr                         5.2.0
rxjs                               6.5.2
typescript                         3.4.5
webpack                            4.30.0

Anything else relevant?
I try to use on different CLI projects a Profile module from a CLI library (all on the same CLI project). This is not possible since angular 8.0.0

Activity

  1. trotyl commented on Jun 7, 2019

    @trotyl
    Contributor

    It's a known limitation:

    Declaration syntax: It's important to follow the route declaration syntax loadChildren: () => import('...').then(m => m.ModuleName) to allow ngc to discover the lazy-loaded module and the associated NgModule. You can find the complete list of allowed syntax constructs here. These restrictions will be relaxed with the release of Ivy since it'll no longer use NgFactories.

    See https://angular.io/guide/deprecations#loadchildren-string-syntax for more information.

    If you believe this is worthing implementing please consider open a feature request to Angular CLI.

  2. robinComa commented on Jun 7, 2019

    @robinComa
    Author

    @trotyl thank you for quick answer,

    I already use this kind of declaration :

    export function loadProfile() {
      return import('shell').then(m => m.ProfileModule);
    }
    
    const routes: Routes = [{
      path: '',
      canActivate: [SecureGuard],
      children: [{
        path: '',
        component: HomeComponent
      }, {
        path: 'profile',
        loadChildren: loadProfile
      }, {
        path: '**',
        component: HomeComponent
      }]
    }];

    If I declare it like that, I have a build issue with ng factory don't exist (I cannot use the Ivy build system, they are issue with the library build

    const routes: Routes = [{
      path: '',
      canActivate: [SecureGuard],
      children: [{
        path: '',
        component: HomeComponent
      }, {
        path: 'profile',
        loadChildren: import('shell').then(m => m.ProfileModule)
      }, {
        path: '**',
        component: HomeComponent
      }]
    }];

    It is an issue with the CLI ? I have to add a bug on the cli project ?

  3. trotyl commented on Jun 7, 2019

    @trotyl
    Contributor

    loadChildren: import('shell').then(m => m.ProfileModule)

    This is not lazy loading at all, you need to wrap an arrow function.

  4. robinComa commented on Jun 7, 2019

    @robinComa
    Author

    Sure, it is a bad copy paste @trotyl

    const routes: Routes = [{
      path: '',
      canActivate: [SecureGuard],
      children: [{
        path: '',
        component: HomeComponent
      }, {
        path: 'profile',
        loadChildren: () => import('shell').then(m => m.ProfileModule)
      }, {
        path: '**',
        component: HomeComponent
      }]
    }];

    If I don't declare it as an exported fonction, the build failed, because there is no ng factory for the module

  5. robinComa commented on Jun 7, 2019

    @robinComa
    Author

    I create same issue on CLI project : #14700

  6. trotyl commented on Jun 7, 2019

    @trotyl
    Contributor

    Is shell also a copy-paste issue? You cannot load an NgModule from node_modules, it must belong to your project.

  7. robinComa commented on Jun 7, 2019

    @robinComa
    Author

    @trotyl, shell is a library from the same project, here the tsconfig file, so it is not a node_module, but an angular library :

    {
      "compileOnSave": false,
      "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "module": "esNext",
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "importHelpers": true,
        "target": "es5",
        "typeRoots": [
          "node_modules/@types"
        ],
        "lib": [
          "es2018",
          "dom"
        ],
        "paths": {
          "shell": [
            "dist/shell"
          ],
          "shell/*": [
            "dist/shell/*"
          ]
        }
      }
    }
    

    and the piece of angular.json about the shell library :

    "shell": {
          "root": "projects/shell",
          "sourceRoot": "projects/shell/src",
          "projectType": "library",
          "prefix": "lib",
          "schematics": {
            "@schematics/angular:component": {
              "styleext": "scss",
              "spec": false
            },
            "@schematics/angular:directive": {
              "spec": false
            },
            "@schematics/angular:modules": {
              "spec": false
            },
            "@schematics/angular:pipe": {
              "spec": false
            },
            "@schematics/angular:service": {
              "spec": false
            },
            "@schematics/angular:class": {
              "spec": false
            }
          },
          "architect": {
            "build": {
              "builder": "@angular-devkit/build-ng-packagr:build",
              "options": {
                "tsConfig": "projects/shell/tsconfig.lib.json",
                "project": "projects/shell/ng-package.json"
              }
            },
            "test": {
              "builder": "@angular-devkit/build-angular:karma",
              "options": {
                "main": "projects/shell/src/test.ts",
                "tsConfig": "projects/shell/tsconfig.spec.json",
                "karmaConfig": "projects/shell/karma.conf.js"
              }
            },
            "lint": {
              "builder": "@angular-devkit/build-angular:tslint",
              "options": {
                "tsConfig": [
                  "projects/shell/tsconfig.lib.json",
                  "projects/shell/tsconfig.spec.json"
                ],
                "exclude": [
                  "**/node_modules/**"
                ]
              }
            }
          }
        }
  8. trotyl commented on Jun 7, 2019

    @trotyl
    Contributor

    Sorry for not making it clear, whether it's from node_modules doesn't matter, from your configuration:

    "shell": [
       "dist/shell"
    ],

    It's not the same project, but a separately built library. (Where the source code resides doesn't make any difference)

  9. robinComa commented on Jun 7, 2019

    @robinComa
    Author

    ok @trotyl and @alan-agius4 , I already try it yesterday @alan-agius4 :

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    import { SecureGuard } from 'shell';
    
    import { HomeComponent } from './home/home.component';
    import { ProfileWrapperModule } from './shared/profile-wrapper/profile-wrapper.module';
    
    const routes: Routes = [{
      path: '',
      canActivate: [SecureGuard],
      children: [{
        path: '',
        component: HomeComponent
      }, {
        path: 'profile',
        loadChildren: () => ProfileWrapperModule // For this feature I don't care about lazy loading, I just reuse module for all my portal apps
      }, {
        path: '**',
        component: HomeComponent
      }]
    }];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    
    import { ProfileModule } from 'shell';
    
    @NgModule({
      declarations: [],
      imports: [
        CommonModule,
        ProfileModule
      ]
    })
    export class ProfileWrapperModule { }

    And it produce the same error

    @alan-agius4 , you can close other issue if you want

    For this 2 ways, that works on dev mode, build AOT is ok, but it is broke on runtime when I try to load the module

  10. alan-agius4 commented on Jun 11, 2019

    @alan-agius4
    Collaborator

    HI, the loadChildren syntax is your example above is not correct.

    It should be:

     loadChildren: () => import('./shared/profile-wrapper/profile-wrapper.module').then(m => m.ProfileWrapperModule) 
  11. robinComa commented on Jun 13, 2019

    @robinComa
    Author

    @alan-agius4 , I have the same error than the 2 others ways :

    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    
    import { ProfileModule } from 'shell';
    
    @NgModule({
      declarations: [],
      imports: [
        CommonModule,
        ProfileModule
      ]
    })
    export class ProfileWrapperModule { }
    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    
    import { SecureGuard } from 'shell';
    
    import { HomeComponent } from './home/home.component';
    
    export function loadProfile() {
      return import('./shared/profile-wrapper.module').then(m => m.ProfileWrapperModule);
    }
    
    const routes: Routes = [{
      path: '',
      canActivate: [SecureGuard],
      children: [{
        path: '',
        component: HomeComponent
      }, {
        path: 'profile',
        loadChildren: loadProfile
      }, {
        path: '**',
        component: HomeComponent
      }]
    }];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }

    Always good on local env, but not on AOT env.
    When a click on the menu to go to my lazy loaded route, the lazy script .js is loaded but it seems to be empty :

    (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{Lz8n:function(n,o,r){"use strict";r.r(o),r.d(o,"ProfileWrapperModule",function(){return u});var u=function(){return function(){}}()}}]);

    And an error appear on the console :

    Uncaught Error: Uncaught (in promise): Error: Runtime compiler is not loaded
    Error: Runtime compiler is not loaded
        at e.Jr (main.b3a8916c121eaf6af8dc.js:1)
        at t.project (main.b3a8916c121eaf6af8dc.js:1)
        at t._tryNext (main.b3a8916c121eaf6af8dc.js:1)
        at t._next (main.b3a8916c121eaf6af8dc.js:1)
        at t.next (main.b3a8916c121eaf6af8dc.js:1)
        at main.b3a8916c121eaf6af8dc.js:1
        at e.invoke (polyfills.06b93379260cc1d8e708.js:1)
        at Object.onInvoke (main.b3a8916c121eaf6af8dc.js:1)
        at e.invoke (polyfills.06b93379260cc1d8e708.js:1)
        at t.run (polyfills.06b93379260cc1d8e708.js:1)
        at Z (polyfills.06b93379260cc1d8e708.js:1)
        at Z (polyfills.06b93379260cc1d8e708.js:1)
        at polyfills.06b93379260cc1d8e708.js:1
        at e.invokeTask (polyfills.06b93379260cc1d8e708.js:1)
        at Object.onInvokeTask (main.b3a8916c121eaf6af8dc.js:1)
        at e.invokeTask (polyfills.06b93379260cc1d8e708.js:1)
        at t.runTask (polyfills.06b93379260cc1d8e708.js:1)
        at g (polyfills.06b93379260cc1d8e708.js:1)
    
  12. alan-agius4 commented on Jun 13, 2019

    @alan-agius4
    Collaborator

    Can you try to see if it works if you remove the loadProfile and pass an arrow function directly to loadChildren as per my previous example?

  13. robinComa commented on Jun 13, 2019

    @robinComa
    Author

    Not possible to build in AOT mode if I pass it directly as an arrow function.

  14. alan-agius4 commented on Jun 13, 2019

    @alan-agius4
    Collaborator

    @robinComa, the following syntax is supported by the AOT compiled

    loadChildren: () => import('./shared/profile-wrapper/profile-wrapper.module')
        .then(m => m.ProfileWrapperModule) 

    Check the lazy loading docs here: https://angular.io/guide/lazy-loading-ngmodules

  15. robinComa commented on Jun 13, 2019

    @robinComa
    Author

    Yes that works ! (it was not working on AOT when it is not proxyfied).
    To conclude, the solution to load a library module is :

    • Proxify the library module inside a project modul
    • Use the import notation of the module
    • Don't use an exported function (like AOT service configuration), but directly an arrow function
    loadChildren: () => import('./shared/profile-wrapper/profile-wrapper.module')
        .then(m => m.ProfileWrapperModule) 
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    
    import { ProfileModule } from 'shell'; // From Angular project library
    
    @NgModule({
      declarations: [],
      imports: [
        CommonModule,
        ProfileModule
      ]
    })
    export class ProfileWrapperModule { }

    Thanks for support !

  16. CSchulz commented on Jul 29, 2019

    @CSchulz

    @alan-agius4 Is support for other import variants planned? I didn't find any issue stating support for different kinds of imports for lazy children.

    It seems lazy children are limited to the application type and do not support libraries, right?
    We have our router module in one library, because the setup is always the same and we don't want to duplicate it for every application.

  17. alan-agius4 commented on Jul 29, 2019

    @alan-agius4
    Collaborator

    Hi @CSchulz, that is correct, lazy loading routes are not supported in libraries, and at the moment there are no concrete plans yet to support this feature.

    There is a greater discussion around this here: #6373 (comment)

  18. CSchulz commented on Jul 29, 2019

    @CSchulz

    @alan-agius4 Thanks for your fast reply. I have seen that discussion.

    To support libraries the libraries needs to provide the factories itself? I have tried to investigate what is missing for supporting it.

  19. 82antu commented on Sep 8, 2019

    @82antu

    I know it's closed but, M I the only one that are is not able to let it work on AOT or PROD ?? proxyfied or not, inside the project or from node_modules AOT (or PROD) compilation always give me "Error: Runtime compiler is not loaded".

    Thanks in advance

  20. akvaliya commented on Sep 20, 2019

    @akvaliya

    I am also getting same error when adding Dynamic Component. What is the resolution for this?

  21. BerBevans commented on Oct 4, 2019

    @BerBevans

    Anyone still having this problem, you might be having the same problem as me and I found the answer here:

    In summary, I was using backticks rather than single quotes when giving the path to the module I wanted to load.

  22. phetw commented on Oct 9, 2019

    @phetw

    @BerBevans It works like a charm. Thanks for pointing this out.

  23. angular-automatic-lock-bot commented on Nov 9, 2019

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

  24. locked and limited conversation to collaborators on Nov 9, 2019
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions