You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
When built with optimization compileModuleAndAllComponentsSync returns an empty list of component factories #36255
This seems to be the behavoir since at least 9.0.1.
Description
Calling the Compiler.compileModuleAndAllComponentsSync method is supposed to return the component factories in addition to the module factory. However, when built with --prod, that list is empty.
Looking over the compiled code, it looks like the declarations are completely removed from the module. Setting 'optimization' to false in angular.json solves the problem, but that means the code is no longer built for production.
Just npm install and ng serve. Clicking on either "Load module 1" or "Load module 2" will call the compileModuleAndAllComponentsSync on a lazy loaded module and display a list of components it declares. However, if started with ng serve --prod, the list will be empty.
🔥 Exception or Error
No error is given, the list of component factories is just empty
The Angular compiler generates AOT code that does retain this information through the setNgModuleScope function, but it's guarded by the ngJitMode global. It appears that the CLI unconditionally removes this call and it doesn't seem possible today to configure this behaviour.
This PR in the CLI seems relevant: angular/angular-cli#16694. @clydin can you comment on any plans in the CLI regarding JIT mode interop with AOT compiled code?
I guess the behaviour is intentional. What I find confusing is that the behaviour is different between production and dev, not between AOT and JIT.
What I'd like to know is if there is any way to get what components are declared in a module (even by using internal API). And if not, if it is intentional, or if there would be a way in the near future.
If you create a production build, it is generally assumed that you don't use JIT aspects to allow for much more effective tree-shaking. This is controlled using the ngJitMode variable, which is new to Ivy and can be used to mix AOT and JIT compilations. As mentioned above, the CLI always assumes this flag is false, so it will get rid of the JIT compatibility code. I don't know whether there's plans to influence this behaviour in the CLI, hence the question to @clydin.
I would be interested to learn about your use-case, though. Could you elaborate on how you're using these APIs? Do you depend on JIT compilation using platformBrowserDynamic, or are you only using this API to get the components of a module without necessarily needing JIT compilations?
It's the latter. We do not want to compile anything. I didn't find any way to lazy load an NgModule. And noticed that the router does this using the compiler (which doesn't actually compile anything in AOT mode :)), so I did the same.
As others have mentioned, with optimizations on the part of the NgModule definition which refers to components/etc. is removed. This allows components to be tree-shaken (since otherwise they would never be removed as modules would contain references to every component).
If you have a use case where you want to load a module and enumerate some components from it, I suggest making a custom multi-provider token and having the module register each component type via the multi-provider (sort of like how the ROUTES token works in the router). You can then load the module, create an injector from it, inject the token, and get a list of components that way. This won't be affected by the optimizer as you're writing your own references to the components, not relying on the module definition.
Yup, that sounds reasonable. Still, from an API point of view, it's weird. I can understand this feature not being available with AOT at all, but having it work or not based on whether you use optimization or not is confusing.
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
Activity
JoostK commentedon Mar 26, 2020
The Angular compiler generates AOT code that does retain this information through the
setNgModuleScopefunction, but it's guarded by thengJitModeglobal. It appears that the CLI unconditionally removes this call and it doesn't seem possible today to configure this behaviour.This PR in the CLI seems relevant: angular/angular-cli#16694. @clydin can you comment on any plans in the CLI regarding JIT mode interop with AOT compiled code?
AndreiVajnaII commentedon Mar 31, 2020
I guess the behaviour is intentional. What I find confusing is that the behaviour is different between production and dev, not between AOT and JIT.
What I'd like to know is if there is any way to get what components are declared in a module (even by using internal API). And if not, if it is intentional, or if there would be a way in the near future.
JoostK commentedon Mar 31, 2020
If you create a production build, it is generally assumed that you don't use JIT aspects to allow for much more effective tree-shaking. This is controlled using the
ngJitModevariable, which is new to Ivy and can be used to mix AOT and JIT compilations. As mentioned above, the CLI always assumes this flag isfalse, so it will get rid of the JIT compatibility code. I don't know whether there's plans to influence this behaviour in the CLI, hence the question to @clydin.I would be interested to learn about your use-case, though. Could you elaborate on how you're using these APIs? Do you depend on JIT compilation using
platformBrowserDynamic, or are you only using this API to get the components of a module without necessarily needing JIT compilations?AndreiVajnaII commentedon Mar 31, 2020
It's the latter. We do not want to compile anything. I didn't find any way to lazy load an NgModule. And noticed that the router does this using the compiler (which doesn't actually compile anything in AOT mode :)), so I did the same.
alxhub commentedon May 26, 2020
As others have mentioned, with optimizations on the part of the NgModule definition which refers to components/etc. is removed. This allows components to be tree-shaken (since otherwise they would never be removed as modules would contain references to every component).
If you have a use case where you want to load a module and enumerate some components from it, I suggest making a custom multi-provider token and having the module register each component type via the multi-provider (sort of like how the
ROUTEStoken works in the router). You can then load the module, create an injector from it, inject the token, and get a list of components that way. This won't be affected by the optimizer as you're writing your own references to the components, not relying on the module definition.AndreiVajnaII commentedon May 29, 2020
Yup, that sounds reasonable. Still, from an API point of view, it's weird. I can understand this feature not being available with AOT at all, but having it work or not based on whether you use optimization or not is confusing.
alan-agius4 commentedon Nov 10, 2020
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 commentedon Dec 11, 2020
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.