nest
nest copied to clipboard
[9.x.x] Duplicate Provider names don't get shown in REPL-debug() function
Is there an existing issue for this?
- [X] I have searched the existing issues
Current behavior
Given the following Diagram:
graph TD;
AppModule((AppModule))-->CatModule((CatModule))
AppModule-->AppService_A>AppService]
AppModule-->foo_A>provide: 'Foo', useValue: 'bar']
CatModule-->AppService_B>AppService]
CatModule-->foo_B>provide: 'Foo', useValue: 'barbaz']
CatModule-->CatService>CatService]
Both the AppModule
and CatModule
have providers with the same name but different implementations/instances.
When using the NestJS repl()
method and print a debug()
statement in order to list the application context, the AppService
& Foo
provider does not get listed within the CatModule
.
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG [NestFactory] Starting Nest application...
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG [InstanceLoader] CatModule dependencies initialized
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG [InstanceLoader] AppModule dependencies initialized
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG REPL initialized
> debug()
AppModule:
- controllers:
â—» AppController
- providers:
â—» AppService
â—» Foo
CatModule:
- providers:
â—» CatService
Minimum reproduction code
https://github.com/BrunnerLivio/nestjs-repro-duplicate-provider-repl
Steps to reproduce
- npm ci
- npm run start:dev
- Write
debug()
within REPL console
Expected behavior
Should list all providers
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG [NestFactory] Starting Nest application...
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG [InstanceLoader] CatModule dependencies initialized
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG [InstanceLoader] AppModule dependencies initialized
[Nest] 5340 - 07/08/2022, 11:18:42 AM LOG REPL initialized
$ debug()
AppModule:
- controllers:
â—» AppController
- providers:
â—» AppService
â—» Foo
CatModule:
- providers:
â—» CatService
+ â—» AppService
+ â—» Foo
Package
- [ ] I don't know. Or some 3rd-party package
- [ ]
@nestjs/common
- [X]
@nestjs/core
- [ ]
@nestjs/microservices
- [ ]
@nestjs/platform-express
- [ ]
@nestjs/platform-fastify
- [ ]
@nestjs/platform-socket.io
- [ ]
@nestjs/platform-ws
- [ ]
@nestjs/testing
- [ ]
@nestjs/websockets
- [ ] Other (see below)
Other package
No response
NestJS version
9.0.0
Packages versions
platform-express version : 9.0.0
schematics version : 9.0.1
testing version : 9.0.0
common version : 9.0.0
core version : 9.0.0
cli version : 9.0.0
Node.js version
16.15.0
In which operating systems have you tested?
- [ ] macOS
- [X] Windows
- [ ] Linux
Other
No response
Sorry for this issue. You just released an AWESOME Nest V9 and now I already have to report an edge case :P Low prio obviously (in case you consider this as unexpected behavior), I am just playing dumb user haha
I was expecting a lot of edge cases for the REPL tbh
I'll investigate this issue tomorrow
Should be easy to fix, luckily. Thank you @BrunnerLivio!
One thing to keep in mind; the REPL seems to automatically load in all the providers & modules into the REPL-context. Though since there are two providers with the same name the following is ambigous (given the example above):
// main.ts
await repl(AppModule);
...
// REPL
[Nest] 5922 - 07/08/2022, 2:17:14 PM LOG REPL initialized
$ methods(AppService)
Methods:
+ â—» getHello
If the CatModule is loaded instead:
// main.ts
await repl(CatModule);
...
// REPL
[Nest] 5922 - 07/08/2022, 2:17:14 PM LOG REPL initialized
$ methods(AppService)
Methods:
+ â—» getDog
I believe it picks the "nearest" provider first? I think if await repl(AppModule)
is executed one wouldn't be able to target AppService
of CatModule
, right? Maybe we should consider a REPL command like this:
> module(CatModule).get(AppService).methods()
Though I leave it up to you to decide. In the end I believe this is an edge-case. Not a lot of people would fall into this behavior.
Maybe we should consider a REPL command like this:
This is already feasible with select
(instead of module()
)
@kamilmysliwiec Yeah i saw that -- though even with select(CatModule).get(AppService)
it will select the AppService
of AppModule
- not the one from CatModule
.
Technically it's possible to get to the actual AppService
of CatModule
like this:
> const AppService_Cat = await import('./dist/cat/app.service.js').then(m => m.AppService)
> methods(AppService_Cat)
Methods:
â—» getDog
> select(CatModule).get(AppService_Cat).getDog()
'Bob'
Though thinking further about this case - I think it makes sense the way it functions now (or at least I can't figure out a better way). So let's ignore that ^
another thing on this issue:
it is always selecting the provider registered on CatModule
@micalevisk what Module is in your example being loaded via await repl(…)
?
I'm using your repro repo. It is repl(AppModule)
if we drop the this.globalScope[stringifiedToken]
assertion below and change that configurable
to true
, debug()
will display the desired output
https://github.com/nestjs/nest/blob/34cb6001c30b3e2d2a9570bc87c6bd78a8c49524/packages/core/repl/repl-context.ts#L81-L87 https://github.com/nestjs/nest/blob/34cb6001c30b3e2d2a9570bc87c6bd78a8c49524/packages/core/repl/repl-context.ts#L91
but that didn't solve this another issue:
> $('Foo')
barbaz
it should display Bar
as we called repl(AppModule)
. As of now we're always overwritting the last provider that has the same stringified token
@BrunnerLivio
though even with
select(CatModule).get(AppService)
it will select theAppService
ofAppModule
- not the one fromCatModule
.
we need to supply { strict: true }
to the get()
otherwise the last (or the first?) registered one will be taken.
I thought this was the default behavior but looks like it's not:
demo
import { Module, Injectable, Controller, Get, Query } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
@Injectable()
class FooService {}
@Module({
providers: [{ provide: 'qux', useValue: 'bar' }, { provide: FooService, useValue: 'bar-service' }]
})
export class BarModule {}
@Module({
imports: [BarModule],
providers: [FooService, { provide: 'qux', useValue: 'foo' }],
})
export class FooModule {}
async function bootstrap() {
const app = await NestFactory.create(FooModule)
await app.init()
const ctxFooModule = app.select(FooModule)
const ctxBarModule = app.select(BarModule)
console.log(
ctxFooModule.get('qux') // 'bar'
,
ctxFooModule.get('qux', { strict: true }) // 'foo'
,
ctxFooModule.get('qux') == ctxBarModule.get('qux') // true
,
ctxFooModule.get('qux',{strict:true}) == ctxBarModule.get('qux',{strict:true}) // false
)
}
bootstrap();
due to the above described behavior, $(duplicatedToken)
ends up returning the wrong provider like so:
but if we use strict: true
below, we got a Nest could not find x element
https://github.com/nestjs/nest/blob/81df0dde0881b0680808798565bc88a0e65dc2ed/packages/core/repl/native-functions/get-relp-fn.ts#L14-L15
Guys, what's the solution for this issue? I can code it but I didn't understood what needs to be done
@ologbonowiwi another bug that I believe isn't directly related to this issue is that: when we have a provider with the same token registered in multiple modules, the latest registered one is always selected when calling get("token")
even if we call it like this:
select(AppModule).get("token")
an workaround to that is select(AppModule).get("token", {strict:true})
@micalevisk that's how NestApplicationContext
instance behaves by default (this isn't a bug)