ng-packagr: Rollup config error on dynamic import during library build
Type of Issue
[x ] Bug Report
[ ] Feature Request
Description
This bug relates to building angular library which uses dynamic imports.
Consider building foo library with the command ng build foo.
When the library contains a file (service in my case), which uses dynamic import, the build will fail throwing error below.
Error: You must set "output.dir" instead of "output.file" when generating multiple chunks.
at error (c:\Development\rollup-test\node_modules\rollup\dist\rollup.js:3410:30)
at normalizeOutputOptions (c:\Development\rollup-test\node_modules\rollup\dist\rollup.js:17107:13)
at getOutputOptions (c:\Development\rollup-test\node_modules\rollup\dist\rollup.js:16865:24)
at Object.write (c:\Development\rollup-test\node_modules\rollup\dist\rollup.js:16957:43)
at Object. (c:\Development\rollup-test\node_modules\ng-packagr\lib\flatten\rollup.js:46:22)
at Generator.next ()
at fulfilled (c:\Development\rollup-test\node_modules\ng-packagr\lib\flatten\rollup.js:4:58)
How To Reproduce
See details about how to reproduce at this post: https://github.com/angular/angular-cli/issues/14444#issue-444906345
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 38
- Comments: 51 (3 by maintainers)
I agree with @blemaire. In a plugin context you want the library to be autonomous. Even more when you deal with non routed lazy-loaded component.
If you take my exemple in earlier post, why a plugin button which call a modal could not take the responsability of it ? THIS look like a bad pattern to me.
The issue may come from https://github.com/ng-packagr/ng-packagr/blob/master/src/lib/flatten/rollup.ts#L69 when there is dynamic import, rollup will try to use
output.dirinstead ofoutput.fileto build multiple bundles.For me, the library I am trying to build is not entirely depends on Angular. So I just use typescript-library-starte and override the https://github.com/alexjoverm/typescript-library-starter/blob/master/rollup.config.ts#L15 to
output.dirthen will have the following issue https://github.com/rollup/rollup/issues/2072 which just says umd bundle format is not compatible for dynamic import. As I googled what that means, one of github issue saysIIFE and UMD formats are (purposefully) self-containedhttps://github.com/lukeed/navaid/issues/5#issuecomment-488540276It seems a lot of effort to make umd support dynamic import. For now I guess the best way right now is to support rollup build multiple bundles and add a ng-packagr option to disable umd build.
@alan-agius4 what if the library is simply a plugin which contains its own routing? Per instance I could be writing a messaging plugin which is meant to be imported and used by several applications. Correct me but a plugin should really be a library (as it is meant to be used in a parent module). Why couldn’t we have lazy loaded routes within this plugin?
There is no way we can do code splitting in angular libraries. I am creating a library of features which needs to be loaded on demand and not eagerly. Any update on this?
Any movement on this?
@alan-agius4 I need to import a button module in a header, based on user role / build config, and then load another part of the library on click. But basically, the dynamic import in a lazy-loaded library is a sweet feature when you deep-dive into code-splitting.
Guys, I really need that 😉 And IMO I am not alone
@alan-agius4
Same here. Here is a use case :
In my case I’m using Transloco library to load i18n files embedded in my library.
This feature, named “Inline Loader” is used with a dynamic import :
Then we do something like this :
This technique works perfectly in the main app.
But when I’m using Ng-packager to build my library, I get the error " Dynamic import is only supported when ‘–module’ flag is ‘commonjs’ or ‘esNext’."
My tsconfig already use esnext and ng-packagr won’t let me build my library.
I don’t know what to do at the moment.
Hi. i’m facing the same problem in our company projects. @JairoMarques said that we can solve this by something like:
Is this solution appropriate? What we lose here by using this?
I didn’t try but i guess you get your DsApplicationsModule bundle in your umd ? So you will lose lazy-load purpose here.
I agree with @blemaire and @raphael22 . If you think this is an incorrect pattern, you could write it in document as best practices or just give warnings. Since the feature is useful in some situations, it should not be missed in code. Just like many deprecated code. Developers are not recommended to use deprecated code but it is still in the framework.
Same here. My library has new Angular 8 syntax:
loadChildren: () => import('./applications/applications.module').then(m => m.DsApplicationsModule)that now doesn’t work.
But works with:
Same problem here, I’m working on a company product which is huge. We want to split it in several libraries so we can sell the product with only those features needed.
Each feature, if required for the project, will be loaded on its own route and should be able to load all its pages in that context. Some features may have a large amount of pages so lazy loading is a must.
Any chance of getting this soon?
My Case:
Writing a simple content management system for re-use across multiple sites. The consuming site configures a route to point to the CMS. Within that route, the CMS is fully functional with its own set of child routes for managing pages and content. The CMS is built as an angular 9 library.
Pages are assembled by selecting from a set of UI components. Any page can have any set of components. I want all my components to be lazy-loaded to minimize overhead and load only what the page data calls for.
Likewise, there are editor components for each UI component. Those must be lazy-loaded within the CMS itself to allow the editor to move elements up and down and edit content.
The CMS is a library using Angular 9 and I’m trying to do an ng build and get the ‘output.dir’ error because it’s trying to output multiple chunks.
Any help on this would be greatly appreciated. Thanks!
I am not too keen about adding support for this feature, and the main reason for this is that IMHO it will make it easier for misuses and incorrect patterns.
The main point of this feature request seems to have lazy loaded modules in a library, which really libraries shouldn’t have! It should be the responsibility of the application to load a module from a library lazily or not, if you want a library to be more granular and tree shakable you should opt for secondary entry points.
Route declaration should happen at an application level.
Related also to: https://github.com/angular/angular/issues/31893#issuecomment-516615243
Hi all,
Closing the issue as at the moment lazy-loading is not designed to work at a library level.
See: https://github.com/angular/angular/issues/31893#issuecomment-516615243 and https://github.com/angular/angular-cli/issues/6373
In future this might be possible when Ivy libraries are published. But until then this is not a viable approach with View Engine.
Really worked for me… Good Idea. Thanks
@daiscog and others struggling: One thing that I forgot to mention is that I created secondary entrypoints that contain the route definitions. Maybe that helps your case too? That way, my assumption goes, the application only has to load that small part eagerly, while the larger chunk of feature module itself stays lazily loaded.
Directory structure looks something like
The
application_routes.tsthen looks likeNote that the application does not directly import anything from the main entry point of the features, only from the secondary routing entrypoint. The application does not have to be in the same workspace, this works exactly the same with the features uploaded as npm packages.
Unsure how well this translates to your use cases, hope you find this helpful!
@cwc-products yeah, definitely, although I’m not sure if that’s what actually does the trick, I’m very new to Angular, just trying to make an inherited, fairly complex application work. Maybe creating the workspace from scratch reset some old configs. I thought I would be stuck on 8 due to this issue, but then I needed Ivy, and then I needed a bugfix in Ivy that was not backported to 8, so I decided to bite the bullet and update to 9, disabling lazy loading as a tradeoff…just to discover that the dynamic syntax suddenly just works ™️
@napstr sounds like your recommendation is to upgrade to Angular 9. FYI, I just finished upgrading to angular 8.
@Azbesciak maybe this will help you…
I disabled umd in ng-packard and I was able to keep lazy loading in angular library:
node_modules/ng-packagr/lib/flatten/rollup.jsnode_modules/ng-packagr/lib/ng-package/entry-point/write-bundles.transform.jscomment this code:@raphael22 You’re right! Just work, but without Lazy-load