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;
+}
+```
+
diff --git a/packages/eslint-plugin/src/rules/no-input-rename.ts b/packages/eslint-plugin/src/rules/no-input-rename.ts
index d1f216b19..aa81e9a55 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](
@@ -98,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',
@@ -147,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',
@@ -182,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
);
});
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;
+
+ }
+ `,
+ })),
+ }),
];