From c687b866b3844915e8aacdd1a0a71563ded9fbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kr=C3=BCger?= Date: Mon, 3 Jul 2023 18:43:30 +0200 Subject: [PATCH] fix(eslint): fix inject function based injection not detecting store (#3936) Closes #3834 --- .../spec/rules/no-store-subscription.spec.ts | 60 ++++++++++++++++++- .../src/utils/helper-functions/utils.ts | 26 +++++++- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/modules/eslint-plugin/spec/rules/no-store-subscription.spec.ts b/modules/eslint-plugin/spec/rules/no-store-subscription.spec.ts index e083e02ad1..a7cc38a6eb 100644 --- a/modules/eslint-plugin/spec/rules/no-store-subscription.spec.ts +++ b/modules/eslint-plugin/spec/rules/no-store-subscription.spec.ts @@ -58,6 +58,33 @@ class Ok4 { this.items$ = store.pipe(select(selectItems)) this.metrics$ = store.select(selectMetrics) } +}`, + ` +import { Store } from '@ngrx/store' +import { inject } from '@angular/core' + +class Ok5 { + readonly items$: Observable + readonly metrics$: Observable + readonly store = inject(Store) + + constructor() { + this.items$ = store.pipe(select(selectItems)) + this.metrics$ = store.select(selectMetrics) + } +}`, + ` +import { Store } from '@ngrx/store' +import { inject } from 'some-other-package' + +class Ok6 { + readonly items$: Observable + readonly metrics$: Observable + readonly store = inject(Store) + + constructor() { + store.pipe(select(selectItems)).subscribe() + } }`, ]; @@ -136,7 +163,7 @@ class NotOk6 { fromFixture(` import { Store } from '@ngrx/store' -class NotOk6 { +class NotOk7 { readonly items: readonly Item[] constructor(store: Store) { @@ -147,7 +174,7 @@ class NotOk6 { fromFixture(` import { Store } from '@ngrx/store' -class NotOk7 { +class NotOk8 { readonly control = new FormControl() constructor(store: Store) { @@ -156,6 +183,35 @@ class NotOk7 { ~~~~~~~~~ [${messageId}] }) } +}`), + fromFixture(` +import { Store } from '@ngrx/store' +import { inject } from '@angular/core' + +class NotOk9 { + readonly control = new FormControl() + store = inject(Store) + + constructor() { + this.control.valueChanges.subscribe(() => { + store.pipe(select(selectItems)).subscribe() + ~~~~~~~~~ [${messageId}] + }) + } +}`), + fromFixture(` +import { Store } from '@ngrx/store' +import { inject } from '@angular/core' + +class NotOk10 { + readonly items$: Observable + readonly metrics$: Observable + readonly store = inject(Store) + + constructor() { + store.pipe(select(selectItems)).subscribe() + ~~~~~~~~~ [${messageId}] + } }`), ]; diff --git a/modules/eslint-plugin/src/utils/helper-functions/utils.ts b/modules/eslint-plugin/src/utils/helper-functions/utils.ts index 31eee60fd3..84bf983638 100644 --- a/modules/eslint-plugin/src/utils/helper-functions/utils.ts +++ b/modules/eslint-plugin/src/utils/helper-functions/utils.ts @@ -13,10 +13,10 @@ import { isProgram, isProperty, isPropertyDefinition, - isTemplateElement, - isTemplateLiteral, isTSTypeAnnotation, isTSTypeReference, + isTemplateElement, + isTemplateLiteral, } from './guards'; import { NGRX_MODULE_PATHS } from './ngrx-modules'; @@ -30,7 +30,8 @@ type InjectedParameter = | ConstructorFunctionExpression | (TSESTree.TSParameterProperty & { parent: ConstructorFunctionExpression; - }); + }) + | TSESTree.PropertyDefinition; }; type InjectedParameterWithSourceCode = Readonly<{ identifiers?: readonly InjectedParameter[]; @@ -315,6 +316,12 @@ function getInjectedParametersWithSourceCode( const { importSpecifier } = getImportDeclarationSpecifier(importDeclarations, importName) ?? {}; + const injectImportDeclarations = + getImportDeclarations(sourceCode.ast, '@angular/core') ?? []; + + const { importSpecifier: injectImportSpecifier } = + getImportDeclarationSpecifier(injectImportDeclarations, 'inject') ?? {}; + if (!importSpecifier) { return { sourceCode }; } @@ -335,6 +342,19 @@ function getInjectedParametersWithSourceCode( return identifiers.concat(parent.parent.parent as InjectedParameter); } + if ( + parent && + isCallExpression(parent) && + isIdentifier(parent.callee) && + parent.callee.name == 'inject' && + parent.parent && + isPropertyDefinition(parent.parent) && + isIdentifier(parent.parent.key) && + injectImportSpecifier + ) { + return identifiers.concat(parent.parent.key as InjectedParameter); + } + return identifiers; }, []); return { identifiers, sourceCode };