#Injection does not work when building modules with SWC

1 messages · Page 1 of 1 (latest)

old pendant
#

We use a monorepo (managed by Turborepo) to develop several independent NPM packages, that are shared across our services. For the sake of this question let's call them @company/module-a and @company/module-b.

When I import @company/module-a in my @company/app in the app.module.ts:

@module({
  imports: [ModuleA]
})

It works. I can inject the services from ModuleA into my app and use them.

However, when ModuleA has a dependency on ModuleB like this: (module-a.module.ts)

@module({
  imports: [ModuleB],
  providers: [ModuleAService],
  exports: [ModuleASerivce]
})

and the ModuleAService injects a service from ModuleB like this:

import { ModuleBService } from '@company/module-b'
@Injectable()
export class ModuleAService {
  public constructor(private readonly moduleBService: ModuleBService) {}
}

the moduleBService is always undefined.

The Nest.js Injections seems not to be able to find ModuleBService. Not even when pointing it to it with:

public constructor(@Inject(ModuleBService.name) private readonly moduleBService: ModuleBService) {}

Then Nest.js throws an error, that it cannot find ModuleBService and if its module is correctly imported.

Is this a known bug? Are we doing something wrong here?

hardy crown
#

Are the monorepo packages installed in each other via npm link or similar?

old pendant
#

yes. This is handeled by Turborepo. @company/module-b is a dependency of ModuleA.

#

I made @nestjs/common a peerDependency as mentioned somewhere on Github.

hardy crown
#

Any chance you can show a minimum reproduction of this?

old pendant
#

yes. I'll try to provide one.

old pendant
hardy crown
#

You shouldn't be using import type, it should just be import, otherwise the desired metadata disappears

old pendant
#

This was an oversight. I removed it. It still does not work (repo updated)

#
/injection-issue/packages/module-a/src/module-a.service.ts:11
│ [1]     const helloB = this.moduleBService.getHello();
│ [1]                                        ^
│ [1] TypeError: Cannot read properties of undefined (reading 'getHello')
│ [1]     at ModuleAService.getHello
hardy crown
#

Okay, that would seem like injection is not happening at all/is being seen as undefined.

How are you testing this at the moment, just through the app?

old pendant
#

I also used the repl to see if moduleB was import (it is imported). I am frankly out of ideas on how to investigate this further.

#

If I do this:

constructor(@Inject(ModuleBService.name) private readonly moduleBService: ModuleBService) {

I get the following error:

ERROR [ExceptionHandler] Nest can't resolve dependencies of the ModuleAServic
│ e (?). Please make sure that the argument "ModuleBService" at index [0] is available in the ModuleAModule context.
│ [1]
│ [1] Potential solutions:
│ [1] - Is ModuleAModule a valid NestJS module?
│ [1] - If "ModuleBService" is a provider, is it part of the current ModuleAModule?
│ [1] - If "ModuleBService" is exported from a separate @Module, is that module imported within ModuleAModule?
│ [1]   @Module({
│ [1]     imports: [ /* the Module containing "ModuleBService" */ ]
│ [1]   })
│ [1]
│ [1] Error: Nest can't resolve dependencies of the ModuleAService (?). Please make sure that the argument "ModuleBService"
│ at index [0] is available in the ModuleAModule context.
hardy crown
#

I'll have to run this soon, currently just debugging from my phone

old pendant
#

It seems to be a metadata issue of the Injector. Maybe I do not build the package correctly. Any pointers how to debug the injector?

#

No rush @hardy crown Your help is much appreciated ❤️
Let me know when you have time to look at the code from a machine.

old pendant
#

Has anyone any idea on how to debug this further?

shut path
#

I'm getting this error instead:

#

it works if I use:

constructor(@Inject(ModuleBService) private readonly moduleBService: ModuleBService) {}
#

so I'd say that something is messing up with the metadata
probably not related with nestjs

#

I see that you're using turbo
So this seems to be a tooling issue

#

Injection does not work when using Turbo

old pendant
#

I debugged further, and I think it is related to SWC, to be honest, and how the modules are built. When I switch to TSC it works.
I'll investigate further and post the result here.

#

Injection does not work when building modules with SWC

old pendant
#

The issue is swc. It does not create declaration files, and this seems to be an issue for Nest.js

#

By compiling the modules with TSC everything works.

hardy crown
#

I wouldn't expect the declaration files to be a problem

#

Rather, the metadata set in the compiled files should be verified

old pendant
#

I added those settings to the swcrc and now it seems to work as well:

"target": "es2021",
    "keepClassNames": true,
    "transform": {
      "decoratorMetadata": true,
      "legacyDecorator": true
    }