Angular 12 library with storybook: Cannot access 'component' before initialization
See original GitHub issueDescribe the bug
This bug only happens when setting the path in tsconfig.json to the source files of the library instead the dist file
To make an Angular library works smoothly with the app live reload, I followed this SO answer https://stackoverflow.com/a/65866136/1015648, in short, I needed to replace the default paths of the library in tsconfig.json
with the source library files instead of usin the dist path.
When I added the storybook, it builds successfully but throws a runtime error:
bootstrap:27 Uncaught ReferenceError: Cannot access 'CarouselNav' before initialization
at Object../projects/ng-gallery/src/lib/carousel/carousel-nav/carousel-nav.component.ts (carousel-nav.component.ts:26)
at __webpack_require__ (bootstrap:24)
at fn (hot module replacement:61)
at Object../projects/ng-gallery/src/lib/carousel/carousel.module.ts (carousel.model.ts:37)
at __webpack_require__ (bootstrap:24)
at fn (hot module replacement:61)
at Object../projects/ng-gallery/src/lib/carousel/index.ts (centralised-slider.ts:24)
at __webpack_require__ (bootstrap:24)
at fn (hot module replacement:61)
at Object../projects/ng-gallery/src/lib/components/gallery.component.ts (gallery-thumbs.component.ts:15)
The error is thrown because of injecting the parent component in a directive which works fine outside the storybook
@Directive({
selector: '[carouselNavNextButton]'
})
export class CarouselNavNextButton {
constructor(@Inject(forwardRef(() => CarouselNav)) public parent: CarouselNav) {
}
}
{
"compilerOptions": {
"paths": {
"ng-gallery": [
"projects/ng-gallery/src/lib"
],
"ng-gallery/*": [
"projects/ng-gallery/src/lib/*"
]
}
}
To Reproduce
ng g lib my-lib
Go to projects /my-lib/src/lib
directory and create index.ts
file which exports lib components that are meant to be publicly available
Edit the projects /my-lib/src/public-api.ts
file in a way it exports all from the previously created index.ts
file, e.g.: export * from './lib/index';
{
"compilerOptions": {
"paths": {
"my-lib": [
"projects/my-lib/src/lib"
],
"my-lib/*": [
"projects/my-lib/src/lib/*"
]
}
}
System
Environment Info:
System:
OS: macOS 11.5
CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
Binaries:
Node: 14.16.1 - /usr/local/bin/node
npm: 6.14.13 - /usr/local/bin/npm
Browsers:
Chrome: 92.0.4515.107
Edge: 92.0.902.62
Safari: 14.1.2
npmPackages:
@storybook/addon-actions: ^6.4.0-alpha.22 => 6.4.0-alpha.22
@storybook/addon-essentials: ^6.4.0-alpha.22 => 6.4.0-alpha.22
@storybook/addon-links: ^6.4.0-alpha.22 => 6.4.0-alpha.22
@storybook/angular: ^6.4.0-alpha.22 => 6.4.0-alpha.22
@storybook/builder-webpack5: ^6.4.0-alpha.22 => 6.4.0-alpha.22
@storybook/manager-webpack5: ^6.4.0-alpha.22 => 6.4.0-alpha.22
Additional context
Issue Analytics
- State:
- Created 2 years ago
- Reactions:8
- Comments:10 (3 by maintainers)
@SarcevicAntonio Thanks for the simple repro. As @salmoro said, the problem is the circular dependency and I also would most likely fix the problem with InjectionToken’s.
It has been a while since I have run into this problem and I don’t remember why Angular’s compiler allows this, but the circular dependency is obvious in that small repro. I ran into many of those circular dependencies scattered among many files when pulling parts of my app into a library and since then I have been very careful to avoid dealing with that again. If you were to build the library with ng-packagr then you would see warnings telling you where the circular dependency is. Surprisingly, the built library seems to actually work for that one. Normally when I ignore those warnings, my apps that import the package have various errors that don’t clearly point out the problem, which is why this issue isn’t obvious that the circular dependency is the problem.
I forked your repro in a Stackblitz WebContainer, with one way I may fix the problem. https://stackblitz.com/edit/github-knujip?preset=node
For reference, and in case the WebContainer doesn’t work, I will explain what I did.
In
ChildComponent
there are references toLibComponent
and inLibComponent
there are references toChildComponent
, which is the circular problem, because which one should be first?First I created a new file
projects/lib/src/lib/container-accessor.ts
and defined anInjectionToken
. I also defined an interface for the instance being injected to implement. That isn’t necessary, but to avoid typing the injected instance asany
or potentially hitting the circular dependency again. It also just abstracts the implementation, to where you could swap the container to anything else implementing the interface without updating your child components.I made
LibComponent
provide itself as tokenCONTAINER_ACCESSOR
. It is still in theInjector
, like it was before where it is provided as it’s class, but now it is also provided by the tokenCONTAINER_ACCESSOR
. The new tokenCONTAINER_ACCESSOR
isn’t defined by either class, so there isn’t a circular dependency.When providing with an
InjectionToken
, I don’t think there is a way to just use types. So,@Inject
will be used to tell theInjector
which token to search for when injecting. (I also removed the redundant instance variable declaration, because adding public, private, or protected makes constructor args instance variables. Since@Optional
was used, I made the type more accurate and added?
. Thenreadonly
is just my preference, because I don’t want anyone to reassign a variable that was injected.)As for why Storybook’s build doesn’t work, but Angular’s does, I would need to dig through what all is being done. Storybook merges necessary parts of Angular’s Webpack config, but I don’t know if it is something in Storybook’s config that isn’t allowing that circular dependency or something that isn’t being used from Angular’s config. Either way, if you don’t avoid the circular dependency, you will probably run into problems later on when the project grows and Angular’s builder can’t figure out how to deal with the circular dependency either.
I have experienced similar errors when I had circular dependencies in my code. I’d get the same runtime error as mentioned by @MurhafSousli and the stories for which those errors were thrown did not show up in the storybook dashboard. After removing the circular dependencies (usually via injection tokens) the errors were gone and the stories appeared.
The annoying part of this is that it’s inconsistent with the angular build which works well in-spite of these circular dependencies.