Angular 9 library with subentry points circular dependency
Asked Answered
J

1

9

I have a very specific question about angular libraries secondary entry points setup. I really don't understand how i can setup it to get it work when they depend on each other including the main entry point. I've read the docs for the ng-packagr and a lot of issues and stack questions but found no really good answer. The thing is that i want to break up our large grown internal library into smaller pieces so that the imports and dependencies getting smaller for apps that don't need everything.

So here is just what i want to reach:

  • Main lib @my/my-lib
  • secondary path @my/my-lib/functions
  • secondary path @my/my-lib/constants
  • secondary path @my/my-lib/lang
  • secondary path @my/my-lib/broker
  • secondary path @my/my-lib/signalr
  • secondary path @my/my-lib/sso
  • secondary path @my/my-lib/types

and thats the folder structure:

projects\my-lib
-- constants\
---- ...
---- package.json
---- public_api.ts
-- functions\
---- ...
---- package.json
---- public_api.ts
-- lang
---- ...
---- package.json
---- public_api.ts
-- broker
---- ...
---- package.json
---- public_api.ts
-- signalr
---- ...
---- package.json
---- public_api.ts
-- sso
---- ...
---- package.json
---- public_api.ts
-- src <-- the main entry point, as setup from the ng g library
---- lib
------ modules <-- the old ones from where i want to source parts out in secondary paths
-------- auth
-------- config
-------- footer
-------- header
-------- log
-------- state
-------- ...
---- public_api.ts
-- ng-package.json <-- main entry point
-- package.json <-- main entry point

Now here is my problem:

The first two, constants and functions, are working just as expected, because they have no dependencies on anything.

Now when i want to import something from @my/my-lib/lang in the main @my/my-lib and reverse i get a circular dependency warning on itself. That sounds in the first place logical for me because the ng-packagr don't know which to build first.

What i've read so far was that the secondary entry points getting build first everytime, this would work perfectly when i dont't have dependencies from @my/my-lib/lang to services back inside @my/my-lib, so how can i setup this that i can import things in @my/my-lib/lang from @my/my-lib and reverse?

J answered 27/3, 2020 at 12:55 Comment(2)
The circular dependency is likely to happen in your scenario. What you need to do is import the @my/my-lib/lang in @my/my-lib. Then if you required some of the functionalities from any other libraries, then import that directly in @my/my-lib/lang instead importing @my/my-lib.Karlotte
Have you tried with peerDependencies?Triste
T
8

From my experience the secondary entry points are built after the main entry point if you are using Angular CLI. The only possible references you can have is to reference the main library from the secondary entry points. You can have references between the secondary entry points again only in one direction. During build the references are resolved and the build order of secondary entry points is determined so they are build in the order of reference.

For your example the only thing you can do is import things from @my/my-lib inside the @my/my-lib/lang and then in @my/my-lib/lang import for example the @my/my-lib/types.

The import should be to the library not the file directly.

import { MainLibClass } from '@my/my-lib';
import { MyType} from '@my/my-lib/types';

Secondary entry points are like another independent library inside the main library that builds on top of it or that provides some independent functionality that is connected to the main entry point. That is why like with libraries you can never have a reference in both directions.

More I cannot say since you don't provide any information why you need references to go in both directions. From what I see from your design it is probably better you create separate libraries for for each part you have now made a secondary entry point.

Triboelectricity answered 5/4, 2020 at 21:27 Comment(9)
Well the thing is that i don't want to complete re-design the actual main library structure. I want only to outsource some specific parts to secondary entry points that dependencies only gets loaded with the secondary entry point, e.g. when i have something like oauth2 or signalr. Let me give you a simple example. I have a service MyLoggingService. This sits inside the main lib. But this service is a core dependency through the whole library including some secondary entry points. Is it right when i say i have to move this part also to it's own secondary entry point like @my/my-lib/logging?J
No you can can reference a service from the main library inside a secondary entry point as i describe in the answer. You don't need to move it to a secondary entry point. I guess you already figured it out since you accepted my answer.Stinnett
No i gave you the bounty because your answer is the only and detailed, but not the solution. The problem when i import from the main library leads to the circular dependency, because the sencondary entry point will be imported by the main library. I have to import MyLoggingService from @my/my-lib withing MyLangugeService from @my/my-lib/lang, but the complete @my/my-lib/lang is a dependency to some modules inside @my/my-lib, for example the MyLayoutsModule for translation. That leads me to the thought, that i have to move all the cross dependencies to their own secondary entry point.J
You cannot import secondary entry points in the main library at least for me this does not work at all even if you don't have the reference in both direction. You need to put all the common things in the main library and then use them in the secondary entry points. Probably the functionality in the main library that needs the language service should be also a secondary entry point and then you can reference it there. I would need to see your design to be able to say more what can be done.Stinnett
Well my picture above tells you the actual design. Everything is under src/lib/modules/my-feature-module and they depend some times on each other, especially code parts from src/lib/modules/config/*, src/lib/modules/broker/* src/lib/modules/state/* and so on and on. That's the old grown structure i want to split out some parts, so that the third party dependencies like @microsoft/signalr or angular-oauth2-oidc only gets loaded when needed and not with the entire library.J
Yes I have seen the picture but as you said you want to import from secondary entry points into the main library which does not work. So you need to keep the common parts in the main library and then just use them in the secondary entry points when needed. One different possibility is that you provide abstract base classes as service interfaces in the main library and then the secondary entry points provide the actual implementation. In this case you will have to provide the services from secondary entry points as providers for the base class in the application providers setup.Stinnett
"but as you said you want to import from secondary entry points into the main library which does not work" ... i think that's the main issue. I have to move all necessary parts into their own secondary parts or more important pieces to it's own primary path like angular does, e.g. @angular/core, @angular/commonJ
Yes I think you should move the pieces like the logging you mentioned in a separate library (primary path). Then you can reference it from the primary and secondary entry points in the library.Stinnett
That's it? Thank you so much. I'm not focus.Stinger

© 2022 - 2024 — McMap. All rights reserved.