From 857928a5d6af58329328672d56976bb086699252 Mon Sep 17 00:00:00 2001 From: Adrian Baran Date: Thu, 17 Nov 2022 15:14:55 -0600 Subject: [PATCH 1/3] fix(no-input-rename): allow input aliases that match the directive name applied to an element --- .../src/rules/no-input-rename.ts | 11 ++++- .../tests/rules/no-input-rename/cases.ts | 47 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-input-rename.ts b/packages/eslint-plugin/src/rules/no-input-rename.ts index d1f216b19..1da6e4bf5 100644 --- a/packages/eslint-plugin/src/rules/no-input-rename.ts +++ b/packages/eslint-plugin/src/rules/no-input-rename.ts @@ -55,13 +55,21 @@ export default createESLintRule({ create(context, [{ allowedNames = [] }]) { let selectors: ReadonlySet = new Set(); const ariaAttributeKeys = getAriaAttributeKeys(); + let selectorDirectiveName: string; return { [Selectors.COMPONENT_OR_DIRECTIVE_SELECTOR_LITERAL]( node: TSESTree.Literal | TSESTree.TemplateElement, ) { + const nodeRawText = ASTUtils.getRawText(node); + const bracketMatchResults = nodeRawText.match(/\[(.*?)\]/); + + if (bracketMatchResults) { + selectorDirectiveName = bracketMatchResults[1]; + } + selectors = new Set( - withoutBracketsAndWhitespaces(ASTUtils.getRawText(node)).split(','), + withoutBracketsAndWhitespaces(nodeRawText).split(','), ); }, [Selectors.INPUT_ALIAS]( @@ -85,6 +93,7 @@ export default createESLintRule({ ); if ( + aliasName === selectorDirectiveName || allowedNames.includes(aliasName) || (ariaAttributeKeys.has(aliasName) && propertyName === kebabToCamelCase(aliasName)) diff --git a/packages/eslint-plugin/tests/rules/no-input-rename/cases.ts b/packages/eslint-plugin/tests/rules/no-input-rename/cases.ts index 880201f69..9cfbd6e53 100644 --- a/packages/eslint-plugin/tests/rules/no-input-rename/cases.ts +++ b/packages/eslint-plugin/tests/rules/no-input-rename/cases.ts @@ -146,6 +146,22 @@ export const valid = [ @Input('fooMyColor') myColor: string; } `, + ` + @Directive({ + selector: 'img[fooDirective]' + }) + class Test { + @Input foo: Foo; + } + `, + ` + @Directive({ + selector: 'img[fooDirective]' + }) + class Test { + @Input('fooDirective') foo: Foo; + } + `, ]; export const invalid = [ @@ -447,4 +463,35 @@ export const invalid = [ `, })), }), + convertAnnotatedSourceToFailureCase({ + description: + 'should fail if input property alias does not match the directive name when applied to an element in the selector', + annotatedSource: ` + @Directive({ + selector: 'img[fooDirective]', + }) + class Test { + @Input('notFooDirective') foo: Foo; + ~~~~~~~~~~~~~~~~~ + } + `, + messageId, + suggestions: ( + [ + [suggestRemoveAliasName, 'foo'], + [suggestReplaceOriginalNameWithAliasName, 'notFooDirective'], + ] as const + ).map(([messageId, propertyName]) => ({ + messageId, + output: ` + @Directive({ + selector: 'img[fooDirective]', + }) + class Test { + @Input() ${propertyName}: Foo; + + } + `, + })), + }), ]; From 63128f1271d432781cf2a089d74451087a31cf41 Mon Sep 17 00:00:00 2001 From: Adrian Baran Date: Thu, 17 Nov 2022 15:17:06 -0600 Subject: [PATCH 2/3] chore(no-input-rename): update rule docs --- .../docs/rules/no-input-rename.md | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/packages/eslint-plugin/docs/rules/no-input-rename.md b/packages/eslint-plugin/docs/rules/no-input-rename.md index eaa56a41e..4bae04702 100644 --- a/packages/eslint-plugin/docs/rules/no-input-rename.md +++ b/packages/eslint-plugin/docs/rules/no-input-rename.md @@ -365,6 +365,38 @@ class Test { } ``` +
+ +--- + +
+ +#### Default Config + +```json +{ + "rules": { + "@angular-eslint/no-input-rename": [ + "error" + ] + } +} +``` + +
+ +#### ❌ Invalid Code + +```ts +@Directive({ + selector: 'img[fooDirective]', +}) +class Test { + @Input('notFooDirective') foo: Foo; + ~~~~~~~~~~~~~~~~~ +} +``` +
@@ -887,6 +919,68 @@ class Test { } ``` +
+ +--- + +
+ +#### Default Config + +```json +{ + "rules": { + "@angular-eslint/no-input-rename": [ + "error" + ] + } +} +``` + +
+ +#### ✅ Valid Code + +```ts +@Directive({ + selector: 'img[fooDirective]' +}) +class Test { + @Input foo: Foo; +} +``` + +
+ +--- + +
+ +#### Default Config + +```json +{ + "rules": { + "@angular-eslint/no-input-rename": [ + "error" + ] + } +} +``` + +
+ +#### ✅ Valid Code + +```ts +@Directive({ + selector: 'img[fooDirective]' +}) +class Test { + @Input('fooDirective') foo: Foo; +} +``` +
From 81012f9b9dc4027d1a3f39fc38823a61b20316d5 Mon Sep 17 00:00:00 2001 From: Adrian Baran Date: Thu, 17 Nov 2022 15:49:20 -0600 Subject: [PATCH 3/3] refactor(no-input-rename): move logic to isAliasNameAllowed --- .../src/rules/no-input-rename.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-input-rename.ts b/packages/eslint-plugin/src/rules/no-input-rename.ts index 1da6e4bf5..aa81e9a55 100644 --- a/packages/eslint-plugin/src/rules/no-input-rename.ts +++ b/packages/eslint-plugin/src/rules/no-input-rename.ts @@ -93,7 +93,6 @@ export default createESLintRule({ ); if ( - aliasName === selectorDirectiveName || allowedNames.includes(aliasName) || (ariaAttributeKeys.has(aliasName) && propertyName === kebabToCamelCase(aliasName)) @@ -107,7 +106,14 @@ export default createESLintRule({ messageId: 'noInputRename', fix: (fixer) => fixer.remove(node), }); - } else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) { + } else if ( + !isAliasNameAllowed( + selectors, + propertyName, + aliasName, + selectorDirectiveName, + ) + ) { context.report({ node, messageId: 'noInputRename', @@ -156,7 +162,14 @@ export default createESLintRule({ ASTUtils.getReplacementText(node, propertyName), ), }); - } else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) { + } else if ( + !isAliasNameAllowed( + selectors, + propertyName, + aliasName, + selectorDirectiveName, + ) + ) { context.report({ node, messageId: 'noInputRename', @@ -191,10 +204,12 @@ function isAliasNameAllowed( selectors: ReadonlySet, propertyName: string, aliasName: string, + selectorDirectiveName: string, ): boolean { return [...selectors].some((selector) => { return ( selector === aliasName || + selectorDirectiveName === aliasName || composedName(selector, propertyName) === aliasName ); });