@depup/angular-eslint__eslint-plugin 21.3.1-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/changes.json +10 -0
- package/dist/configs/README.md +44 -0
- package/dist/configs/all.json +54 -0
- package/dist/configs/recommended.json +18 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +145 -0
- package/dist/rules/component-class-suffix.d.ts +15 -0
- package/dist/rules/component-class-suffix.d.ts.map +1 -0
- package/dist/rules/component-class-suffix.js +56 -0
- package/dist/rules/component-max-inline-declarations.d.ts +17 -0
- package/dist/rules/component-max-inline-declarations.d.ts.map +1 -0
- package/dist/rules/component-max-inline-declarations.js +107 -0
- package/dist/rules/component-selector.d.ts +14 -0
- package/dist/rules/component-selector.d.ts.map +1 -0
- package/dist/rules/component-selector.js +218 -0
- package/dist/rules/computed-must-return.d.ts +11 -0
- package/dist/rules/computed-must-return.d.ts.map +1 -0
- package/dist/rules/computed-must-return.js +62 -0
- package/dist/rules/consistent-component-styles.d.ts +12 -0
- package/dist/rules/consistent-component-styles.d.ts.map +1 -0
- package/dist/rules/consistent-component-styles.js +97 -0
- package/dist/rules/contextual-decorator.d.ts +12 -0
- package/dist/rules/contextual-decorator.d.ts.map +1 -0
- package/dist/rules/contextual-decorator.js +61 -0
- package/dist/rules/contextual-lifecycle.d.ts +11 -0
- package/dist/rules/contextual-lifecycle.d.ts.map +1 -0
- package/dist/rules/contextual-lifecycle.js +61 -0
- package/dist/rules/directive-class-suffix.d.ts +13 -0
- package/dist/rules/directive-class-suffix.d.ts.map +1 -0
- package/dist/rules/directive-class-suffix.js +60 -0
- package/dist/rules/directive-selector.d.ts +14 -0
- package/dist/rules/directive-selector.d.ts.map +1 -0
- package/dist/rules/directive-selector.js +156 -0
- package/dist/rules/no-async-lifecycle-method.d.ts +11 -0
- package/dist/rules/no-async-lifecycle-method.d.ts.map +1 -0
- package/dist/rules/no-async-lifecycle-method.js +45 -0
- package/dist/rules/no-attribute-decorator.d.ts +11 -0
- package/dist/rules/no-attribute-decorator.d.ts.map +1 -0
- package/dist/rules/no-attribute-decorator.js +32 -0
- package/dist/rules/no-conflicting-lifecycle.d.ts +11 -0
- package/dist/rules/no-conflicting-lifecycle.d.ts.map +1 -0
- package/dist/rules/no-conflicting-lifecycle.js +74 -0
- package/dist/rules/no-developer-preview.d.ts +11 -0
- package/dist/rules/no-developer-preview.d.ts.map +1 -0
- package/dist/rules/no-developer-preview.js +45 -0
- package/dist/rules/no-duplicates-in-metadata-arrays.d.ts +11 -0
- package/dist/rules/no-duplicates-in-metadata-arrays.d.ts.map +1 -0
- package/dist/rules/no-duplicates-in-metadata-arrays.js +67 -0
- package/dist/rules/no-empty-lifecycle-method.d.ts +11 -0
- package/dist/rules/no-empty-lifecycle-method.d.ts.map +1 -0
- package/dist/rules/no-empty-lifecycle-method.js +75 -0
- package/dist/rules/no-experimental.d.ts +11 -0
- package/dist/rules/no-experimental.d.ts.map +1 -0
- package/dist/rules/no-experimental.js +45 -0
- package/dist/rules/no-forward-ref.d.ts +12 -0
- package/dist/rules/no-forward-ref.d.ts.map +1 -0
- package/dist/rules/no-forward-ref.js +33 -0
- package/dist/rules/no-implicit-take-until-destroyed.d.ts +11 -0
- package/dist/rules/no-implicit-take-until-destroyed.d.ts.map +1 -0
- package/dist/rules/no-implicit-take-until-destroyed.js +83 -0
- package/dist/rules/no-input-prefix.d.ts +13 -0
- package/dist/rules/no-input-prefix.d.ts.map +1 -0
- package/dist/rules/no-input-prefix.js +118 -0
- package/dist/rules/no-input-rename.d.ts +13 -0
- package/dist/rules/no-input-rename.d.ts.map +1 -0
- package/dist/rules/no-input-rename.js +213 -0
- package/dist/rules/no-inputs-metadata-property.d.ts +11 -0
- package/dist/rules/no-inputs-metadata-property.d.ts.map +1 -0
- package/dist/rules/no-inputs-metadata-property.js +53 -0
- package/dist/rules/no-lifecycle-call.d.ts +11 -0
- package/dist/rules/no-lifecycle-call.d.ts.map +1 -0
- package/dist/rules/no-lifecycle-call.js +49 -0
- package/dist/rules/no-output-native.d.ts +11 -0
- package/dist/rules/no-output-native.d.ts.map +1 -0
- package/dist/rules/no-output-native.js +47 -0
- package/dist/rules/no-output-on-prefix.d.ts +11 -0
- package/dist/rules/no-output-on-prefix.d.ts.map +1 -0
- package/dist/rules/no-output-on-prefix.js +48 -0
- package/dist/rules/no-output-rename.d.ts +11 -0
- package/dist/rules/no-output-rename.d.ts.map +1 -0
- package/dist/rules/no-output-rename.js +172 -0
- package/dist/rules/no-outputs-metadata-property.d.ts +11 -0
- package/dist/rules/no-outputs-metadata-property.d.ts.map +1 -0
- package/dist/rules/no-outputs-metadata-property.js +53 -0
- package/dist/rules/no-pipe-impure.d.ts +11 -0
- package/dist/rules/no-pipe-impure.d.ts.map +1 -0
- package/dist/rules/no-pipe-impure.js +42 -0
- package/dist/rules/no-queries-metadata-property.d.ts +11 -0
- package/dist/rules/no-queries-metadata-property.d.ts.map +1 -0
- package/dist/rules/no-queries-metadata-property.js +38 -0
- package/dist/rules/no-uncalled-signals.d.ts +12 -0
- package/dist/rules/no-uncalled-signals.d.ts.map +1 -0
- package/dist/rules/no-uncalled-signals.js +92 -0
- package/dist/rules/pipe-prefix.d.ts +15 -0
- package/dist/rules/pipe-prefix.d.ts.map +1 -0
- package/dist/rules/pipe-prefix.js +92 -0
- package/dist/rules/prefer-host-metadata-property.d.ts +10 -0
- package/dist/rules/prefer-host-metadata-property.d.ts.map +1 -0
- package/dist/rules/prefer-host-metadata-property.js +40 -0
- package/dist/rules/prefer-inject.d.ts +11 -0
- package/dist/rules/prefer-inject.d.ts.map +1 -0
- package/dist/rules/prefer-inject.js +86 -0
- package/dist/rules/prefer-on-push-component-change-detection.d.ts +11 -0
- package/dist/rules/prefer-on-push-component-change-detection.d.ts.map +1 -0
- package/dist/rules/prefer-on-push-component-change-detection.js +81 -0
- package/dist/rules/prefer-output-emitter-ref.d.ts +10 -0
- package/dist/rules/prefer-output-emitter-ref.d.ts.map +1 -0
- package/dist/rules/prefer-output-emitter-ref.js +30 -0
- package/dist/rules/prefer-output-readonly.d.ts +11 -0
- package/dist/rules/prefer-output-readonly.d.ts.map +1 -0
- package/dist/rules/prefer-output-readonly.js +51 -0
- package/dist/rules/prefer-signal-model.d.ts +10 -0
- package/dist/rules/prefer-signal-model.d.ts.map +1 -0
- package/dist/rules/prefer-signal-model.js +71 -0
- package/dist/rules/prefer-signals.d.ts +20 -0
- package/dist/rules/prefer-signals.d.ts.map +1 -0
- package/dist/rules/prefer-signals.js +174 -0
- package/dist/rules/prefer-standalone.d.ts +11 -0
- package/dist/rules/prefer-standalone.d.ts.map +1 -0
- package/dist/rules/prefer-standalone.js +66 -0
- package/dist/rules/relative-url-prefix.d.ts +11 -0
- package/dist/rules/relative-url-prefix.d.ts.map +1 -0
- package/dist/rules/relative-url-prefix.js +59 -0
- package/dist/rules/require-lifecycle-on-prototype.d.ts +11 -0
- package/dist/rules/require-lifecycle-on-prototype.d.ts.map +1 -0
- package/dist/rules/require-lifecycle-on-prototype.js +72 -0
- package/dist/rules/require-localize-metadata.d.ts +17 -0
- package/dist/rules/require-localize-metadata.d.ts.map +1 -0
- package/dist/rules/require-localize-metadata.js +123 -0
- package/dist/rules/runtime-localize.d.ts +11 -0
- package/dist/rules/runtime-localize.d.ts.map +1 -0
- package/dist/rules/runtime-localize.js +45 -0
- package/dist/rules/sort-keys-in-type-decorator.d.ts +16 -0
- package/dist/rules/sort-keys-in-type-decorator.d.ts.map +1 -0
- package/dist/rules/sort-keys-in-type-decorator.js +191 -0
- package/dist/rules/sort-lifecycle-methods.d.ts +11 -0
- package/dist/rules/sort-lifecycle-methods.d.ts.map +1 -0
- package/dist/rules/sort-lifecycle-methods.js +45 -0
- package/dist/rules/use-component-selector.d.ts +11 -0
- package/dist/rules/use-component-selector.d.ts.map +1 -0
- package/dist/rules/use-component-selector.js +41 -0
- package/dist/rules/use-component-view-encapsulation.d.ts +11 -0
- package/dist/rules/use-component-view-encapsulation.d.ts.map +1 -0
- package/dist/rules/use-component-view-encapsulation.js +49 -0
- package/dist/rules/use-injectable-provided-in.d.ts +13 -0
- package/dist/rules/use-injectable-provided-in.d.ts.map +1 -0
- package/dist/rules/use-injectable-provided-in.js +63 -0
- package/dist/rules/use-lifecycle-interface.d.ts +11 -0
- package/dist/rules/use-lifecycle-interface.d.ts.map +1 -0
- package/dist/rules/use-lifecycle-interface.js +65 -0
- package/dist/rules/use-pipe-transform-interface.d.ts +11 -0
- package/dist/rules/use-pipe-transform-interface.d.ts.map +1 -0
- package/dist/rules/use-pipe-transform-interface.js +49 -0
- package/dist/utils/create-eslint-rule.d.ts +8 -0
- package/dist/utils/create-eslint-rule.d.ts.map +1 -0
- package/dist/utils/create-eslint-rule.js +28 -0
- package/dist/utils/jsdoc.d.ts +11 -0
- package/dist/utils/jsdoc.d.ts.map +1 -0
- package/dist/utils/jsdoc.js +174 -0
- package/dist/utils/signals.d.ts +2 -0
- package/dist/utils/signals.d.ts.map +1 -0
- package/dist/utils/signals.js +10 -0
- package/package.json +56 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
6
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
+
exports.RULE_NAME = 'no-input-rename';
|
|
8
|
+
const STYLE_GUIDE_LINK = 'https://angular.dev/guide/components/inputs#choosing-input-names';
|
|
9
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
10
|
+
name: exports.RULE_NAME,
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'suggestion',
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Ensures that input bindings are not aliased',
|
|
15
|
+
recommended: 'recommended',
|
|
16
|
+
},
|
|
17
|
+
fixable: 'code',
|
|
18
|
+
hasSuggestions: true,
|
|
19
|
+
schema: [
|
|
20
|
+
{
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
allowedNames: {
|
|
24
|
+
type: 'array',
|
|
25
|
+
items: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
},
|
|
28
|
+
description: 'A list with allowed input names',
|
|
29
|
+
uniqueItems: true,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
messages: {
|
|
36
|
+
noInputRename: `Input bindings should not be aliased (${STYLE_GUIDE_LINK})`,
|
|
37
|
+
suggestRemoveAliasName: 'Remove alias name',
|
|
38
|
+
suggestReplaceOriginalNameWithAliasName: 'Remove alias name and use it as the original name',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultOptions: [{ allowedNames: [] }],
|
|
42
|
+
create(context, [{ allowedNames = [] }]) {
|
|
43
|
+
let selectors = new Set();
|
|
44
|
+
const ariaAttributeKeys = (0, utils_1.getAriaAttributeKeys)();
|
|
45
|
+
let selectorDirectiveName;
|
|
46
|
+
return {
|
|
47
|
+
[utils_1.Selectors.COMPONENT_OR_DIRECTIVE_SELECTOR_LITERAL](node) {
|
|
48
|
+
const nodeRawText = utils_1.ASTUtils.getRawText(node);
|
|
49
|
+
const bracketMatchResults = nodeRawText.match(/\[(.*?)\]/);
|
|
50
|
+
if (bracketMatchResults) {
|
|
51
|
+
selectorDirectiveName = bracketMatchResults[1];
|
|
52
|
+
}
|
|
53
|
+
selectors = new Set((0, utils_1.withoutBracketsAndWhitespaces)(nodeRawText).split(','));
|
|
54
|
+
},
|
|
55
|
+
[utils_1.Selectors.INPUT_ALIAS](node) {
|
|
56
|
+
const propertyOrMethodDefinition = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isPropertyOrMethodDefinition);
|
|
57
|
+
if (!propertyOrMethodDefinition ||
|
|
58
|
+
!utils_2.ASTUtils.isIdentifier(propertyOrMethodDefinition.key)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const aliasName = utils_1.ASTUtils.getRawText(node);
|
|
62
|
+
const propertyName = utils_1.ASTUtils.getRawText(propertyOrMethodDefinition.key);
|
|
63
|
+
if (allowedNames.includes(aliasName) ||
|
|
64
|
+
(ariaAttributeKeys.has(aliasName) &&
|
|
65
|
+
propertyName === (0, utils_1.kebabToCamelCase)(aliasName))) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// The alias is either a string in the `@Input()` decorator function,
|
|
69
|
+
// or a string on an `alias` property that is in an object expression
|
|
70
|
+
// that is in the `@Input()` decorator or the `input()` function or the
|
|
71
|
+
// `input.required()` function. If it's on the `alias` property, then we
|
|
72
|
+
// want to remove that whole property rather than just the string literal.
|
|
73
|
+
const stringToRemove = utils_1.ASTUtils.isTemplateElement(node)
|
|
74
|
+
? node.parent
|
|
75
|
+
: node;
|
|
76
|
+
let rangeToRemove = stringToRemove.range;
|
|
77
|
+
if (utils_1.ASTUtils.isProperty(stringToRemove.parent)) {
|
|
78
|
+
const property = stringToRemove.parent;
|
|
79
|
+
rangeToRemove = property.range;
|
|
80
|
+
if (utils_1.ASTUtils.isObjectExpression(property.parent)) {
|
|
81
|
+
const objectExpression = property.parent;
|
|
82
|
+
if (objectExpression.properties.length === 1) {
|
|
83
|
+
// The property is the only property in the
|
|
84
|
+
// object, so we can remove the whole object.
|
|
85
|
+
rangeToRemove = objectExpression.range;
|
|
86
|
+
// If the object is in an `input()` function, then
|
|
87
|
+
// the object will be the second argument. The first
|
|
88
|
+
// argument will be the default value. We need to
|
|
89
|
+
// remove the comma after the default value.
|
|
90
|
+
const tokenBefore = context.sourceCode.getTokenBefore(objectExpression);
|
|
91
|
+
if (tokenBefore && utils_2.ASTUtils.isCommaToken(tokenBefore)) {
|
|
92
|
+
rangeToRemove = [tokenBefore.range[0], rangeToRemove[1]];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// There are other properties in the object, so we
|
|
97
|
+
// can only remove the property. How we remove it
|
|
98
|
+
// will depend on where the property is in the object.
|
|
99
|
+
const propertyIndex = objectExpression.properties.indexOf(property);
|
|
100
|
+
if (propertyIndex < objectExpression.properties.length - 1) {
|
|
101
|
+
// The property is not the last one, so we can
|
|
102
|
+
// remove everything up to the next property
|
|
103
|
+
// which will remove the comma after it.
|
|
104
|
+
rangeToRemove = [
|
|
105
|
+
property.range[0],
|
|
106
|
+
objectExpression.properties[propertyIndex + 1].range[0],
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// The property is the last one. If the object has a
|
|
111
|
+
// trailing comma, then we want to keep the trailing comma.
|
|
112
|
+
// The simplest way to do that is to remove the property
|
|
113
|
+
// and the comma that precedes it.
|
|
114
|
+
const tokenBefore = context.sourceCode.getTokenBefore(property);
|
|
115
|
+
if (tokenBefore && utils_2.ASTUtils.isCommaToken(tokenBefore)) {
|
|
116
|
+
rangeToRemove = [tokenBefore.range[0], rangeToRemove[1]];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (aliasName === propertyName) {
|
|
123
|
+
context.report({
|
|
124
|
+
node,
|
|
125
|
+
messageId: 'noInputRename',
|
|
126
|
+
fix: (fixer) => fixer.removeRange(rangeToRemove),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else if (!isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName)) {
|
|
130
|
+
context.report({
|
|
131
|
+
node,
|
|
132
|
+
messageId: 'noInputRename',
|
|
133
|
+
suggest: [
|
|
134
|
+
{
|
|
135
|
+
messageId: 'suggestRemoveAliasName',
|
|
136
|
+
fix: (fixer) => fixer.removeRange(rangeToRemove),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
messageId: 'suggestReplaceOriginalNameWithAliasName',
|
|
140
|
+
fix: (fixer) => [
|
|
141
|
+
fixer.removeRange(rangeToRemove),
|
|
142
|
+
fixer.replaceText(propertyOrMethodDefinition.key, aliasName.includes('-') ? `'${aliasName}'` : aliasName),
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[utils_1.Selectors.INPUTS_METADATA_PROPERTY_LITERAL](node) {
|
|
150
|
+
const ancestorMaybeHostDirectiveAPI = node.parent?.parent?.parent?.parent?.parent;
|
|
151
|
+
if (ancestorMaybeHostDirectiveAPI &&
|
|
152
|
+
utils_1.ASTUtils.isProperty(ancestorMaybeHostDirectiveAPI)) {
|
|
153
|
+
/**
|
|
154
|
+
* Angular v15 introduced the directive composition API: https://angular.dev/guide/directives/directive-composition-api
|
|
155
|
+
* Renaming host directive inputs using this API is not a bad practice and should not be reported
|
|
156
|
+
*/
|
|
157
|
+
const hostDirectiveAPIPropertyName = 'hostDirectives';
|
|
158
|
+
if ((utils_1.ASTUtils.isLiteral(ancestorMaybeHostDirectiveAPI.key) &&
|
|
159
|
+
ancestorMaybeHostDirectiveAPI.key.value ===
|
|
160
|
+
hostDirectiveAPIPropertyName) ||
|
|
161
|
+
(utils_2.ASTUtils.isIdentifier(ancestorMaybeHostDirectiveAPI.key) &&
|
|
162
|
+
ancestorMaybeHostDirectiveAPI.key.name ===
|
|
163
|
+
hostDirectiveAPIPropertyName)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const [propertyName, aliasName] = (0, utils_1.withoutBracketsAndWhitespaces)(utils_1.ASTUtils.getRawText(node)).split(':');
|
|
168
|
+
if (!aliasName ||
|
|
169
|
+
allowedNames.includes(aliasName) ||
|
|
170
|
+
(ariaAttributeKeys.has(aliasName) &&
|
|
171
|
+
propertyName === (0, utils_1.kebabToCamelCase)(aliasName))) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (aliasName === propertyName) {
|
|
175
|
+
context.report({
|
|
176
|
+
node,
|
|
177
|
+
messageId: 'noInputRename',
|
|
178
|
+
fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, propertyName)),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else if (!isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName)) {
|
|
182
|
+
context.report({
|
|
183
|
+
node,
|
|
184
|
+
messageId: 'noInputRename',
|
|
185
|
+
suggest: [
|
|
186
|
+
['suggestRemoveAliasName', propertyName],
|
|
187
|
+
['suggestReplaceOriginalNameWithAliasName', aliasName],
|
|
188
|
+
].map(([messageId, name]) => ({
|
|
189
|
+
messageId,
|
|
190
|
+
fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, name)),
|
|
191
|
+
})),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
'ClassDeclaration:exit'() {
|
|
196
|
+
selectors = new Set();
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
function composedName(selector, propertyName) {
|
|
202
|
+
return `${selector}${(0, utils_1.capitalize)(propertyName)}`;
|
|
203
|
+
}
|
|
204
|
+
function isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName) {
|
|
205
|
+
return [...selectors].some((selector) => {
|
|
206
|
+
return (selector === aliasName ||
|
|
207
|
+
selectorDirectiveName === aliasName ||
|
|
208
|
+
composedName(selector, propertyName) === aliasName);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
212
|
+
rationale: "Renaming inputs creates a confusing situation where the property has one name inside the component class (private name) and a different name in templates (public name). For example, with the @Input() decorator: '@Input(\"userName\") name: string' means you access 'this.name' in the component but bind with '[userName]' in templates. Similarly, with the signal-based input() function: 'userName = input<string>({ alias: \"name\" })' means you access 'this.userName()' internally but bind with '[name]' in templates. This dual naming makes the code harder to understand, complicates refactoring, and can cause bugs when developers forget which name to use in which context. Keep the internal and external names the same unless you have a strong reason for the mismatch.",
|
|
213
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noInputsMetadataProperty';
|
|
3
|
+
export declare const RULE_NAME = "no-inputs-metadata-property";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noInputsMetadataProperty", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=no-inputs-metadata-property.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-inputs-metadata-property.d.ts","sourceRoot":"","sources":["../../src/rules/no-inputs-metadata-property.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,0BAA0B,CAAC;AACpD,eAAO,MAAM,SAAS,gCAAgC,CAAC;;;;AAGvD,wBAoDG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
6
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
+
exports.RULE_NAME = 'no-inputs-metadata-property';
|
|
8
|
+
const METADATA_PROPERTY_NAME = 'inputs';
|
|
9
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
10
|
+
name: exports.RULE_NAME,
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'suggestion',
|
|
13
|
+
docs: {
|
|
14
|
+
description: `Disallows usage of the \`${METADATA_PROPERTY_NAME}\` metadata property`,
|
|
15
|
+
recommended: 'recommended',
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
messages: {
|
|
19
|
+
noInputsMetadataProperty: `Use \`@Input\` rather than the \`${METADATA_PROPERTY_NAME}\` metadata property`,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultOptions: [],
|
|
23
|
+
create(context) {
|
|
24
|
+
return {
|
|
25
|
+
[`${utils_1.Selectors.COMPONENT_OR_DIRECTIVE_CLASS_DECORATOR} ${utils_1.Selectors.metadataProperty(METADATA_PROPERTY_NAME)}`](node) {
|
|
26
|
+
/**
|
|
27
|
+
* Angular v15 introduced the directive composition API: https://angular.dev/guide/directives/directive-composition-api
|
|
28
|
+
* Using host directive inputs using this API is not a bad practice and should not be reported
|
|
29
|
+
*/
|
|
30
|
+
const ancestorMayBeHostDirectiveAPI = node.parent?.parent?.parent;
|
|
31
|
+
if (ancestorMayBeHostDirectiveAPI &&
|
|
32
|
+
utils_1.ASTUtils.isProperty(ancestorMayBeHostDirectiveAPI)) {
|
|
33
|
+
const hostDirectiveAPIPropertyName = 'hostDirectives';
|
|
34
|
+
if ((utils_1.ASTUtils.isLiteral(ancestorMayBeHostDirectiveAPI.key) &&
|
|
35
|
+
ancestorMayBeHostDirectiveAPI.key.value ===
|
|
36
|
+
hostDirectiveAPIPropertyName) ||
|
|
37
|
+
(utils_2.ASTUtils.isIdentifier(ancestorMayBeHostDirectiveAPI.key) &&
|
|
38
|
+
ancestorMayBeHostDirectiveAPI.key.name ===
|
|
39
|
+
hostDirectiveAPIPropertyName)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
context.report({
|
|
44
|
+
node,
|
|
45
|
+
messageId: 'noInputsMetadataProperty',
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
52
|
+
rationale: "Using the 'inputs' metadata property (@Component({ inputs: ['name'] })) is discouraged in favor of modern alternatives like the signal-based input() function (introduced in Angular v17.1) or the @Input() decorator. Modern approaches offer several benefits: (1) they make it immediately clear which properties are part of the component's API when reading the class, (2) configuration happens in one place right next to the property, (3) they work better with IDE features like Find References and Rename Refactoring, (4) input() provides signal-based reactivity with built-in features like required inputs, transforms, and aliases, and (5) @Input() decorator offers similar features for non-signal inputs. The metadata property approach is a legacy pattern that obscures the component's interface and requires looking in two places (decorator metadata and class body) to understand the component's API. For new code, prefer input() for signal-based reactivity or @Input() for traditional inputs.",
|
|
53
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noLifecycleCall';
|
|
3
|
+
export declare const RULE_NAME = "no-lifecycle-call";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noLifecycleCall", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=no-lifecycle-call.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-lifecycle-call.d.ts","sourceRoot":"","sources":["../../src/rules/no-lifecycle-call.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,iBAAiB,CAAC;AAC3C,eAAO,MAAM,SAAS,sBAAsB,CAAC;;;;AAE7C,wBAwCG;AAsBH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const utils_2 = require("@typescript-eslint/utils");
|
|
6
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
+
exports.RULE_NAME = 'no-lifecycle-call';
|
|
8
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
9
|
+
name: exports.RULE_NAME,
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Disallows explicit calls to lifecycle methods',
|
|
14
|
+
},
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
noLifecycleCall: 'Avoid explicit calls to lifecycle methods',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
const angularLifeCycleMethodsPattern = (0, utils_1.toPattern)([
|
|
23
|
+
...utils_1.ASTUtils.ANGULAR_LIFECYCLE_METHODS,
|
|
24
|
+
]);
|
|
25
|
+
return {
|
|
26
|
+
[`ClassDeclaration CallExpression > MemberExpression[property.name=${angularLifeCycleMethodsPattern}]`]: (node) => {
|
|
27
|
+
const classDeclaration = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isClassDeclaration);
|
|
28
|
+
if (!classDeclaration ||
|
|
29
|
+
!utils_1.ASTUtils.getAngularClassDecorator(classDeclaration) ||
|
|
30
|
+
(utils_1.ASTUtils.isSuper(node.object) && isSuperCallAllowed(node))) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
context.report({ node: node.parent, messageId: 'noLifecycleCall' });
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
function hasSameName({ property }, { key }) {
|
|
39
|
+
return (utils_2.ASTUtils.isIdentifier(property) &&
|
|
40
|
+
utils_2.ASTUtils.isIdentifier(key) &&
|
|
41
|
+
property.name === key.name);
|
|
42
|
+
}
|
|
43
|
+
function isSuperCallAllowed(node) {
|
|
44
|
+
const methodDefinition = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isMethodDefinition);
|
|
45
|
+
return Boolean(methodDefinition && hasSameName(node, methodDefinition));
|
|
46
|
+
}
|
|
47
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
48
|
+
rationale: `Lifecycle hooks like ngOnInit(), ngOnDestroy(), and ngOnChanges() are designed to be called automatically by Angular at the appropriate times in a component's lifecycle. Calling them manually (e.g., 'this.ngOnInit()') breaks Angular's lifecycle management, can cause hooks to run multiple times or in the wrong order, and makes the code's execution flow harder to understand. The only exception is calling super.ngOnInit() in a derived class to ensure the parent class's initialization runs. If you need to share initialization logic, extract it into a separate method that both ngOnInit() and your other code can call.`,
|
|
49
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noOutputNative';
|
|
3
|
+
export declare const RULE_NAME = "no-output-native";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noOutputNative", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=no-output-native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-output-native.d.ts","sourceRoot":"","sources":["../../src/rules/no-output-native.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,gBAAgB,CAAC;AAC1C,eAAO,MAAM,SAAS,qBAAqB,CAAC;;;;AAE5C,wBA8CG;AAEH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
exports.RULE_NAME = 'no-output-native';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Ensures that output bindings, including aliases, are not named as standard DOM events',
|
|
13
|
+
recommended: 'recommended',
|
|
14
|
+
},
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
noOutputNative: 'Output bindings, including aliases, should not be named as standard DOM events',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
const nativeEventNames = (0, utils_1.getNativeEventNames)();
|
|
23
|
+
const selectors = [
|
|
24
|
+
utils_1.Selectors.OUTPUTS_METADATA_PROPERTY_LITERAL,
|
|
25
|
+
utils_1.Selectors.OUTPUT_ALIAS,
|
|
26
|
+
utils_1.Selectors.OUTPUT_PROPERTY_OR_GETTER,
|
|
27
|
+
].join(',');
|
|
28
|
+
return {
|
|
29
|
+
[selectors](node) {
|
|
30
|
+
const [propertyName, aliasName] = utils_1.ASTUtils.getRawText(node)
|
|
31
|
+
.replace(/\s/g, '')
|
|
32
|
+
.split(':');
|
|
33
|
+
if (!nativeEventNames.has(propertyName) &&
|
|
34
|
+
!nativeEventNames.has(aliasName)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
context.report({
|
|
38
|
+
node,
|
|
39
|
+
messageId: 'noOutputNative',
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
46
|
+
rationale: `Naming an @Output() the same as a native DOM event (like 'click', 'change', or 'blur') creates ambiguous and confusing template bindings. For example, if a component has '@Output() click', the template '(click)="handler()"' could be binding to either the component's custom output OR the native DOM click event, depending on the context. This ambiguity leads to bugs and makes code harder to understand. Instead, use descriptive names that clearly indicate these are custom component events, such as 'userSelected' or 'itemDeleted' instead of generic names like 'click' or 'select'.`,
|
|
47
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noOutputOnPrefix';
|
|
3
|
+
export declare const RULE_NAME = "no-output-on-prefix";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noOutputOnPrefix", [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=no-output-on-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-output-on-prefix.d.ts","sourceRoot":"","sources":["../../src/rules/no-output-on-prefix.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAC5C,eAAO,MAAM,SAAS,wBAAwB,CAAC;;;;AAI/C,wBA4CG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
exports.RULE_NAME = 'no-output-on-prefix';
|
|
7
|
+
const STYLE_GUIDE_LINK = 'https://angular.dev/guide/components/outputs#choosing-event-names';
|
|
8
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
9
|
+
name: exports.RULE_NAME,
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: `Ensures that output bindings, including aliases, are not named "on", nor prefixed with it. See more at ${STYLE_GUIDE_LINK}`,
|
|
14
|
+
recommended: 'recommended',
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
messages: {
|
|
18
|
+
noOutputOnPrefix: `Output bindings, including aliases, should not be named "on", nor prefixed with it (${STYLE_GUIDE_LINK})`,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
const outputOnPattern = /^on(([^a-z])|(?=$))/;
|
|
24
|
+
const selectors = [
|
|
25
|
+
utils_1.Selectors.OUTPUTS_METADATA_PROPERTY_LITERAL,
|
|
26
|
+
utils_1.Selectors.OUTPUT_ALIAS,
|
|
27
|
+
utils_1.Selectors.OUTPUT_PROPERTY_OR_GETTER,
|
|
28
|
+
].join(',');
|
|
29
|
+
return {
|
|
30
|
+
[selectors](node) {
|
|
31
|
+
const [propertyName, aliasName] = utils_1.ASTUtils.getRawText(node)
|
|
32
|
+
.replace(/\s/g, '')
|
|
33
|
+
.split(':');
|
|
34
|
+
if (!outputOnPattern.test(propertyName) &&
|
|
35
|
+
!outputOnPattern.test(aliasName)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
context.report({
|
|
39
|
+
node,
|
|
40
|
+
messageId: 'noOutputOnPrefix',
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
47
|
+
rationale: "Angular's template syntax allows both '(eventName)' and 'on-eventName' for event bindings. If you prefix an output with 'on', this creates awkward template syntax like '(onUserClick)' or the even more redundant 'on-onUserClick'. This violates the principle of least surprise and makes templates harder to read. Instead, name outputs as simple actions or events (like 'userClick'), which Angular templates will bind as '(userClick)' or 'on-userClick'.",
|
|
48
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noOutputRename' | 'suggestRemoveAliasName' | 'suggestReplaceOriginalNameWithAliasName';
|
|
3
|
+
export declare const RULE_NAME = "no-output-rename";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
|
8
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
9
|
+
rationale: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=no-output-rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-output-rename.d.ts","sourceRoot":"","sources":["../../src/rules/no-output-rename.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAClB,gBAAgB,GAChB,wBAAwB,GACxB,yCAAyC,CAAC;AAC9C,eAAO,MAAM,SAAS,qBAAqB,CAAC;;;;AAI5C,wBAgMG;AAmBH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|