Skip to content

(AngularCompilerPlugin) AOT with lazy modules without angular/router #8142

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
bukharin opened this issue Oct 21, 2017 · 11 comments
Closed

(AngularCompilerPlugin) AOT with lazy modules without angular/router #8142

bukharin opened this issue Oct 21, 2017 · 11 comments

Comments

@bukharin
Copy link

Hi,

I try to enable AOT compilation in our project with lazy modules. We do not use @angular/router and lazy modules loads like this:

require.ensure([], () => {
                        resolve(require("../+profile/profile.module.ts")["ProfileModule"]);
                    }, err => reject(err), "profile");

At runtime I discover that AngularCompilerPlugin do not override ProfileModule to ProfileModuleNgFactory. The plugin can not determine lazy modules in our application because we don't use angular/router and instantinate modules by using angular low level api for that (like router).

How can I achieve this?

I think the best way is to remove explicit binding from @angular/router for determining lazy modules. Also you can provide additional options for explicit lazy modules config.

@clydin
Copy link
Member

clydin commented Oct 22, 2017

It's not happening because AngularCompilerPlugin doesn't do that. SystemJsNgModuleLoader from @angular/core does it at runtime. AngularCompilerPlugin essentially acts as a bridge to allow SystemJsNgModuleLoader to work with Webpack. Since Webpack cannot determine the necessary context from the deep System.import call within SystemJsNgModuleLoader, the AngularCompilerPlugin analyzes the lazy routes to provide the necessary context information to Webpack.
If the application has a completely custom Angular module loading system then it would need to be responsible for handling both JIT and AOT differences.
This would also be the case if an application was using a function with @angular/router's loadChildren to lazy load modules (example in this issue: #7147).

@bukharin
Copy link
Author

@clydin thanks for reply. I give an simple example that illustrates the issue.

My component:

@Component({
   selector: "foo-cmp",
   template: `<button (click)="loadLazyModule()">Load lazy module</button>`
})
export class FooComponent {

   loadLazyModule() {
        require.ensure([], () => {
            const lazyModule = require("../../../+profile/profile.module.ts")["ProfileModule"];
            console.log(lazyModule); // <--- lazyModule is ProfileModule both in JIT and AOT builds
        }, err => console.error(err), "profile");
    }

}

I expect that lazyModule will be a ProfileModuleNgFactory instead of ProfileModule when I build application with AngularCompilerPlugin

I guess it's because AngularCompilerPlugin determine lazy modules using angular/compilier-cli api which looking loadChildren property of specific module (angular/router) -https://github.com/angular/angular/blob/master/packages/compiler-cli/src/ngtools_impl.ts#L27

@bukharin bukharin changed the title (AngularCompilerPlugin) AOT with lazy modules outside of angular/router (AngularCompilerPlugin) AOT with lazy modules without of angular/router Oct 22, 2017
@bukharin bukharin changed the title (AngularCompilerPlugin) AOT with lazy modules without of angular/router (AngularCompilerPlugin) AOT with lazy modules without angular/router Oct 22, 2017
@clydin
Copy link
Member

clydin commented Oct 22, 2017

When using Angular's router (or even SystemJsNgModuleLoader directly), the decision to load the module factory and use it directly or load the module and compile is handled at runtime via the logic found here. Since the example code provided above is using a custom lazy module loading system divorced from Angular's infrastructure, the logic to handle AOT needs to be built into that custom system.

@bukharin
Copy link
Author

@clydin yes we have the same handling logic as angular/router. But I don't understand how can I get NgFactory reference for lazy module at runtime with AngularCompilerPlugin ? Could you provide example for that?

@clydin
Copy link
Member

clydin commented Oct 22, 2017

The AngularCompilerPlugin isn't actually the relevant component. It doesn't make the module/factory loading decision; SystemJsNgModuleLoader does. SystemJsNgModuleLoader is itself a working example of the necessary logic. When a module load is performed, a check is made at runtime to determine the current mode (JIT/AOT) and then the code path splits to either load and compile the module or load a factory for the module. The module factory asset and name are generated as needed from passed module information which can be seen here.

@bukharin
Copy link
Author

@clydin we already have a custom NgModuleFactoryLoader which works similar as SystemJsNgModuleLoader.

SystemJsNgModuleLoader in AOT mode import %modulepath%.ngFactory instead of %modulepath%, right? But who must create %modulepath%.ngFactory for lazy modules during build time? As I see AngularCompilerPlugin do this for entryModule perfectly. It compile entryModule.ngFactory and override bootstrap code to use them.

But in our app nobody creates .ngFactory files for lazy modules. If I try to import lazy.module.ngFactory I receive build time error Module not found: Error: Can't resolve './lazy.module.ngFactory'. If I import lazy.module then I receive runtime error because code expect to receive LazyModuleNgFactory instead of LazyModule in AOT mode.

Also I see that AngularCompilierPlugin discover application lazy routes here and I guess to create a replacement for importing ngfactory path.

@bukharin
Copy link
Author

bukharin commented Oct 23, 2017

Oh, I look at AotPlugin, but I am using AngularCompilerPlugin with Angular 5. Anyway AngularCompilerPlugin uses discovered lazy routes when resolving dependecies

@clydin
Copy link
Member

clydin commented Oct 23, 2017

The AOT compiler will turn example.module.ts into example.module.ngfactory.ts (and eventually example.module.ngfactory.js). The class name will change from ExampleModule to ExampleModuleNgFactory. The new compiler plugin (AngularCompilerPlugin) places the generated files next to the original source within the in-memory build-time filesystem.

The code you're linking to in AngularCompilerPlugin is used when SystemJsNgModuleLoader is being analyzed by webpack. Webpack cannot determine the context for the deep lazy loading import statements it contains. The code section provides the necessary context information to allow SystemJsNgModuleLoader to function with Wepback. If SystemJsNgModuleLoader is not being used by the application that entire section is essentially unused.
Note that if your application is using a deeply nested require/import statement similar to SystemJsNgModuleLoader than you would need to do something similar on the Webpack side that would be based on the operation and logic of the custom routing/lazy loading system in use.

Also, the bootstrapping code is very different than this. The plugin actually performs code refactoring to adjust the bootstrap call. The bootstrap call does not perform lazy loading and as such is using an instance of the module directly. As a result the call must be adjusted to handle AOT. The semantics of the Angular bootstrap call are also well known which allows the refactoring to be safely performed. This is essentially convenience functionality as the manual method of providing two main files as shown in many AOT guides would also function here as well.

@bukharin
Copy link
Author

@clydin I got it! Thank you for explanation.

@mlakmal
Copy link

mlakmal commented Dec 13, 2017

@bukharin were you able to get the angular compiler to compile modules loaded through System.import instead of router definition ?

@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 Sep 7, 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

3 participants