diff --git a/lint/src/rules/ts/themed-component-usages.ts b/lint/src/rules/ts/themed-component-usages.ts index d9cc3127ed..1263e44b48 100644 --- a/lint/src/rules/ts/themed-component-usages.ts +++ b/lint/src/rules/ts/themed-component-usages.ts @@ -335,6 +335,69 @@ cy.get('ds-themeable'); cy.get('#test > ds-themeable > #nest'); `, }, + { + name: 'edge case: unable to find usage node through usage token, but import is still flagged and fixed', + code: ` +import { Component } from '@angular/core'; + +import { Context } from '../../core/shared/context.model'; +import { TestThemeableComponent } from '../test/test-themeable.component.ts'; + +@Component({ + selector: 'ds-admin-search-page', + templateUrl: './admin-search-page.component.html', + styleUrls: ['./admin-search-page.component.scss'], + standalone: true, + imports: [ + TestThemeableComponent + ], +}) + +/** + * Component that represents a search page for administrators + */ +export class AdminSearchPageComponent { + /** + * The context of this page + */ + context: Context = Context.AdminSearch; +} + `, + errors: [ + { + messageId: Message.WRONG_IMPORT, + }, + { + messageId: Message.WRONG_CLASS, + }, + ], + output: ` +import { Component } from '@angular/core'; + +import { Context } from '../../core/shared/context.model'; +import { ThemedTestThemeableComponent } from '../test/themed-test-themeable.component.ts'; + +@Component({ + selector: 'ds-admin-search-page', + templateUrl: './admin-search-page.component.html', + styleUrls: ['./admin-search-page.component.scss'], + standalone: true, + imports: [ + ThemedTestThemeableComponent + ], +}) + +/** + * Component that represents a search page for administrators + */ +export class AdminSearchPageComponent { + /** + * The context of this page + */ + context: Context = Context.AdminSearch; +} + `, + }, ], }; diff --git a/lint/src/util/typescript.ts b/lint/src/util/typescript.ts index 5b7bdb858d..90b0f2c49f 100644 --- a/lint/src/util/typescript.ts +++ b/lint/src/util/typescript.ts @@ -54,6 +54,7 @@ export function findUsages(context: AnyRuleContext, localNode: TSESTree.Identifi for (const token of source.ast.tokens) { if (token.type === 'Identifier' && token.value === localNode.name && !match(token.range, localNode.range)) { const node = source.getNodeByRangeIndex(token.range[0]); + // todo: in some cases, the resulting node can actually be the whole program (!) if (node !== null) { usages.push(node as TSESTree.Identifier); } @@ -64,12 +65,9 @@ export function findUsages(context: AnyRuleContext, localNode: TSESTree.Identifi } export function isPartOfTypeExpression(node: TSESTree.Identifier): boolean { - return node.parent.type.startsWith('TSType'); + return node.parent?.type?.startsWith('TSType'); } export function isPartOfClassDeclaration(node: TSESTree.Identifier): boolean { - if (node.parent === undefined) { - return false; - } - return node.parent.type === 'ClassDeclaration'; + return node.parent?.type === 'ClassDeclaration'; }