Skip to content

Commit 2d20ef9

Browse files
committed
fix(core): improve dependency resolution error messages
1 parent b245ea0 commit 2d20ef9

File tree

3 files changed

+126
-9
lines changed

3 files changed

+126
-9
lines changed

integration/injector/e2e/optional-factory-provider-dep.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ Potential solutions:
127127
@Module({
128128
imports: [ /* the Module containing "MISSING_DEP" */ ]
129129
})
130-
`);
130+
131+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`);
131132
}
132133
});
133134
});

packages/core/errors/messages.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,26 +69,52 @@ export const UNKNOWN_DEPENDENCIES_MESSAGE = (
6969
const moduleName = getModuleName(moduleRef);
7070
const dependencyName = getDependencyName(name, 'dependency');
7171

72-
const potentialSolutions =
73-
// If module's name is well defined
74-
moduleName !== 'current'
75-
? `\n
72+
const isImportTypeIssue =
73+
!isNil(index) &&
74+
dependencies &&
75+
(dependencies[index] === undefined ||
76+
dependencies[index] === Object ||
77+
(typeof dependencies[index] === 'function' &&
78+
(dependencies[index] as any).name === 'Object'));
79+
80+
let potentialSolutions: string;
81+
82+
if (isImportTypeIssue) {
83+
potentialSolutions = `\n
84+
Potential solutions:
85+
- The dependency at index [${index}] appears to be undefined at runtime
86+
- This commonly occurs when using 'import type' instead of 'import' for injectable classes
87+
- Check your imports and change:
88+
❌ import type { SomeService } from './some.service';
89+
✅ import { SomeService } from './some.service';
90+
- Ensure the imported class is decorated with @Injectable() or is a valid provider
91+
- If using dynamic imports, ensure the class is available at runtime, not just for type checking
92+
93+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`;
94+
} else {
95+
potentialSolutions =
96+
// If module's name is well defined
97+
moduleName !== 'current'
98+
? `\n
7699
Potential solutions:
77100
- Is ${moduleName} a valid NestJS module?
78101
- If ${dependencyName} is a provider, is it part of the current ${moduleName}?
79102
- If ${dependencyName} is exported from a separate @Module, is that module imported within ${moduleName}?
80103
@Module({
81104
imports: [ /* the Module containing ${dependencyName} */ ]
82105
})
83-
`
84-
: `\n
106+
107+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`
108+
: `\n
85109
Potential solutions:
86110
- If ${dependencyName} is a provider, is it part of the current Module?
87111
- If ${dependencyName} is exported from a separate @Module, is that module imported within Module?
88112
@Module({
89113
imports: [ /* the Module containing ${dependencyName} */ ]
90114
})
91-
`;
115+
116+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`;
117+
}
92118

93119
let message = `Nest can't resolve dependencies of the ${type.toString()}`;
94120

@@ -103,7 +129,7 @@ Potential solutions:
103129

104130
message += ` (`;
105131
message += dependenciesName.join(', ');
106-
message += `). Please make sure that the argument ${dependencyName} at index [${index}] is available in the ${moduleName} context.`;
132+
message += `). Please make sure that the argument ${isImportTypeIssue ? 'dependency' : dependencyName} at index [${index}] is available in the ${isImportTypeIssue ? 'current' : moduleName} context.`;
107133
message += potentialSolutions;
108134

109135
return message;
@@ -175,6 +201,8 @@ export const UNKNOWN_EXPORT_MESSAGE = (
175201
176202
Possible Solutions:
177203
- Is ${token} part of the relevant providers/imports within ${module}?
204+
205+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
178206
`;
179207
};
180208

packages/core/test/errors/test/messages.spec.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { UnknownDependenciesException } from '../../../errors/exceptions/unknown
33
import {
44
INVALID_MODULE_MESSAGE,
55
UNDEFINED_MODULE_MESSAGE,
6+
UNKNOWN_EXPORT_MESSAGE,
67
} from '../../../errors/messages';
78
import { Module } from '../../../injector/module';
89
import { stringCleaner } from '../../utils/string.cleaner';
@@ -23,6 +24,8 @@ describe('Error Messages', () => {
2324
@Module({
2425
imports: [ /* the Module containing dependency */ ]
2526
})
27+
28+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
2629
`);
2730

2831
class CatService {}
@@ -46,6 +49,8 @@ describe('Error Messages', () => {
4649
@Module({
4750
imports: [ /* the Module containing dependency */ ]
4851
})
52+
53+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
4954
`);
5055

5156
const actualMessage = stringCleaner(
@@ -67,6 +72,8 @@ describe('Error Messages', () => {
6772
@Module({
6873
imports: [ /* the Module containing "FooRepository" */ ]
6974
})
75+
76+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
7077
`);
7178

7279
const actualMessage = stringCleaner(
@@ -89,6 +96,8 @@ describe('Error Messages', () => {
8996
@Module({
9097
imports: [ /* the Module containing dependency */ ]
9198
})
99+
100+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
92101
`);
93102

94103
function CatFunction() {}
@@ -110,6 +119,8 @@ describe('Error Messages', () => {
110119
@Module({
111120
imports: [ /* the Module containing dependency */ ]
112121
})
122+
123+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
113124
`);
114125

115126
const actualMessage = stringCleaner(
@@ -132,6 +143,8 @@ describe('Error Messages', () => {
132143
@Module({
133144
imports: [ /* the Module containing dependency */ ]
134145
})
146+
147+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
135148
`);
136149

137150
class MetaType {
@@ -165,6 +178,8 @@ describe('Error Messages', () => {
165178
@Module({
166179
imports: [ /* the Module containing dependency */ ]
167180
})
181+
182+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
168183
`);
169184

170185
const actualMessage = stringCleaner(
@@ -186,6 +201,8 @@ describe('Error Messages', () => {
186201
@Module({
187202
imports: [ /* the Module containing dependency */ ]
188203
})
204+
205+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
189206
`);
190207

191208
const actualMessage = stringCleaner(
@@ -195,6 +212,77 @@ describe('Error Messages', () => {
195212
}).message,
196213
);
197214

215+
expect(actualMessage).to.equal(expectedResult);
216+
});
217+
it('should detect likely import type issue and provide specific guidance', () => {
218+
const expectedResult =
219+
stringCleaner(`Nest can't resolve dependencies of the ResourceController (ResourceService, ?). Please make sure that the argument dependency at index [1] is available in the current context.
220+
221+
Potential solutions:
222+
- The dependency at index [1] appears to be undefined at runtime
223+
- This commonly occurs when using 'import type' instead of 'import' for injectable classes
224+
- Check your imports and change:
225+
❌ import type { SomeService } from './some.service';
226+
✅ import { SomeService } from './some.service';
227+
- Ensure the imported class is decorated with @Injectable() or is a valid provider
228+
- If using dynamic imports, ensure the class is available at runtime, not just for type checking
229+
230+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
231+
`);
232+
233+
const actualMessage = stringCleaner(
234+
new UnknownDependenciesException('ResourceController', {
235+
index: 1,
236+
dependencies: ['ResourceService', Object], // Object simulates import type issue
237+
name: 'SomeService',
238+
}).message,
239+
);
240+
241+
expect(actualMessage).to.equal(expectedResult);
242+
});
243+
it('should detect import type issue with mixed dependencies', () => {
244+
const expectedResult =
245+
stringCleaner(`Nest can't resolve dependencies of the ResourceController (ValidService, ?, AnotherService). Please make sure that the argument dependency at index [1] is available in the current context.
246+
247+
Potential solutions:
248+
- The dependency at index [1] appears to be undefined at runtime
249+
- This commonly occurs when using 'import type' instead of 'import' for injectable classes
250+
- Check your imports and change:
251+
❌ import type { SomeService } from './some.service';
252+
✅ import { SomeService } from './some.service';
253+
- Ensure the imported class is decorated with @Injectable() or is a valid provider
254+
- If using dynamic imports, ensure the class is available at runtime, not just for type checking
255+
256+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
257+
`);
258+
259+
class ValidService {}
260+
class AnotherService {}
261+
262+
const actualMessage = stringCleaner(
263+
new UnknownDependenciesException('ResourceController', {
264+
index: 1,
265+
dependencies: [ValidService, Object, AnotherService], // mixed valid/Object
266+
name: 'SomeService',
267+
}).message,
268+
);
269+
270+
expect(actualMessage).to.equal(expectedResult);
271+
});
272+
it('should add documentation links to export errors', () => {
273+
const expectedResult =
274+
stringCleaner(`Nest cannot export a provider/module that is not a part of the currently processed module (TestModule). Please verify whether the exported TestService is available in this particular context.
275+
276+
Possible Solutions:
277+
- Is TestService part of the relevant providers/imports within TestModule?
278+
279+
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
280+
`);
281+
282+
const actualMessage = stringCleaner(
283+
UNKNOWN_EXPORT_MESSAGE('TestService', 'TestModule'),
284+
);
285+
198286
expect(actualMessage).to.equal(expectedResult);
199287
});
200288
});

0 commit comments

Comments
 (0)