Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/rules/detect-circular-reference.rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ESLintUtils, type TSESTree } from '@typescript-eslint/utils';

const createRule = ESLintUtils.RuleCreator(
(name) => `https://eslint.org/docs/latest/rules/${name}`
);

export type MessageIds =
| 'serviceCircularDependency'
| 'moduleCircularDependency';

const defaultOptions: unknown[] = [];

export default createRule<unknown[], MessageIds>({
name: 'detect-circular-reference',
meta: {
type: 'problem',
docs: {
description:
'Warns about circular dependencies with forwardRef() function',
recommended: 'recommended',
},
fixable: undefined,
schema: [], // no options
messages: {
serviceCircularDependency: '⚠️ Circular-dependency detected',
moduleCircularDependency: '⚠️ Circular-dependency detected',
},
},
defaultOptions,
create(context) {
return {
'CallExpression > Identifier[name="forwardRef"]': (
node: TSESTree.Identifier
) => {
if (node?.name === 'forwardRef') {
context.report({
messageId: 'serviceCircularDependency',
node,
loc: node.loc,
});
}
},
};
},
});
94 changes: 94 additions & 0 deletions tests/rules/detect-circular-reference.rule.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { RuleTester } from '@typescript-eslint/rule-tester';
import detectCircularReferenceRule from '../../src/rules/detect-circular-reference.rule';

// This test required changes to the tsconfig file to allow importing from the rule-tester package.
// See https://github.com/typescript-eslint/typescript-eslint/issues/7284

const ruleTester = new RuleTester({
parserOptions: {
project: './tsconfig.json',
},
parser: '@typescript-eslint/parser',
defaultFilenames: {
// We need to specify a filename that will be used by the rule parser.
// Since the test process starts at the root of the project, we need to point to the sub folder containing it.
ts: './tests/rules/file.ts',
tsx: '',
},
});

ruleTester.run('detect-circular-reference', detectCircularReferenceRule, {
valid: [
{
code: `
import { forwardRef } from '@nestjs/common';
import { CommonService } from './common.service';
@Injectable()
export class CatsService {
constructor(
private commonService: CommonService,
) {}
}
`,
},
{
code: `
@Module({
imports: [CatsModule], // ⚠️ Circular-dependency detected
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder to remove the comment

})
export class CommonModule {}
`,
},
],
invalid: [
{
code: `
import { forwardRef } from '@nestjs/common';

@Injectable()
export class CatsService {
constructor(
@Inject(forwardRef(() => CommonService)) // ⚠️ Circular-dependency detected
private commonService: CommonService,
) {}
}
`,
errors: [
{
messageId: 'serviceCircularDependency',
},
],
},
// {
// code: `
// import { forwardRef as renamedForwardRef } from '@nestjs/common';

// @Injectable()
// export class CatsService {
// constructor(
// @Inject(renamedForwardRef(() => CommonService)) // ⚠️ Circular-dependency detected
// private commonService: CommonService,
// ) {}
// }
// `,
// errors: [
// {
// messageId: 'serviceCircularDependency',
// },
// ],
// },
// {
// code: `
// @Module({
// imports: [forwardRef(() => CatsModule)], // ⚠️ Circular-dependency detected
// })
// export class CommonModule {}
// `,
// errors: [
// {
// messageId: 'moduleCircularDependency',
// },
// ],
// },
],
});