Skip to content

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

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

Closed
robinComa opened this issue Jun 7, 2019 · 23 comments

Comments

@robinComa
Copy link

🐞 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

@trotyl
Copy link
Contributor

trotyl commented Jun 7, 2019

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.

@robinComa
Copy link
Author

robinComa commented Jun 7, 2019

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

@trotyl
Copy link
Contributor

trotyl commented Jun 7, 2019

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

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

@robinComa
Copy link
Author

robinComa commented Jun 7, 2019

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

@robinComa
Copy link
Author

I create same issue on CLI project : #14700

@trotyl
Copy link
Contributor

trotyl commented Jun 7, 2019

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

@robinComa
Copy link
Author

robinComa commented Jun 7, 2019

@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/**"
            ]
          }
        }
      }
    }

@trotyl
Copy link
Contributor

trotyl commented Jun 7, 2019

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)

@robinComa
Copy link
Author

robinComa commented Jun 7, 2019

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

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Jun 11, 2019

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) 

@robinComa
Copy link
Author

robinComa commented Jun 13, 2019

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

@alan-agius4
Copy link
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?

@alan-agius4 alan-agius4 transferred this issue from angular/angular Jun 13, 2019
@robinComa
Copy link
Author

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

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Jun 13, 2019

@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

@robinComa
Copy link
Author

robinComa commented Jun 13, 2019

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 !

@CSchulz
Copy link

CSchulz commented Jul 29, 2019

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

@alan-agius4
Copy link
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)

@CSchulz
Copy link

CSchulz commented Jul 29, 2019

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

@82antu
Copy link

82antu commented Sep 8, 2019

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

@akvaliya
Copy link

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

@BerBevans
Copy link

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.

@phetw
Copy link

phetw commented Oct 9, 2019

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

@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 Nov 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants