@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,172 @@
|
|
|
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-output-rename';
|
|
8
|
+
const STYLE_GUIDE_LINK = 'https://angular.dev/guide/components/outputs#choosing-event-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 output bindings are not aliased',
|
|
15
|
+
recommended: 'recommended',
|
|
16
|
+
},
|
|
17
|
+
fixable: 'code',
|
|
18
|
+
hasSuggestions: true,
|
|
19
|
+
schema: [],
|
|
20
|
+
messages: {
|
|
21
|
+
noOutputRename: `Output bindings should not be aliased (${STYLE_GUIDE_LINK})`,
|
|
22
|
+
suggestRemoveAliasName: 'Remove alias name',
|
|
23
|
+
suggestReplaceOriginalNameWithAliasName: 'Remove alias name and use it as the original name',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultOptions: [],
|
|
27
|
+
create(context) {
|
|
28
|
+
let selectors = new Set();
|
|
29
|
+
return {
|
|
30
|
+
[utils_1.Selectors.COMPONENT_OR_DIRECTIVE_SELECTOR_LITERAL](node) {
|
|
31
|
+
selectors = new Set((0, utils_1.withoutBracketsAndWhitespaces)(utils_1.ASTUtils.getRawText(node)).split(','));
|
|
32
|
+
},
|
|
33
|
+
[utils_1.Selectors.OUTPUT_ALIAS](node) {
|
|
34
|
+
const propertyOrMethodDefinition = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isPropertyOrMethodDefinition);
|
|
35
|
+
if (!propertyOrMethodDefinition ||
|
|
36
|
+
!utils_2.ASTUtils.isIdentifier(propertyOrMethodDefinition.key)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const aliasName = utils_1.ASTUtils.getRawText(node);
|
|
40
|
+
const propertyName = utils_1.ASTUtils.getRawText(propertyOrMethodDefinition.key);
|
|
41
|
+
// The alias is either a string in the `@Output()` decorator function,
|
|
42
|
+
// or a string on an `alias` property that is in an object expression
|
|
43
|
+
// that is in the `output()` function. If it's the latter, then we want
|
|
44
|
+
// to remove that whole property rather than just the string literal.
|
|
45
|
+
const stringToRemove = utils_1.ASTUtils.isTemplateElement(node)
|
|
46
|
+
? node.parent
|
|
47
|
+
: node;
|
|
48
|
+
let rangeToRemove = stringToRemove.range;
|
|
49
|
+
if (utils_1.ASTUtils.isProperty(stringToRemove.parent)) {
|
|
50
|
+
const property = stringToRemove.parent;
|
|
51
|
+
rangeToRemove = property.range;
|
|
52
|
+
if (utils_1.ASTUtils.isObjectExpression(property.parent)) {
|
|
53
|
+
const objectExpression = property.parent;
|
|
54
|
+
if (objectExpression.properties.length === 1) {
|
|
55
|
+
// The property is the only property in the
|
|
56
|
+
// object, so we can remove the whole object.
|
|
57
|
+
rangeToRemove = objectExpression.range;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// There are other properties in the object, so we
|
|
61
|
+
// can only remove the property. How we remove it
|
|
62
|
+
// will depend on where the property is in the object.
|
|
63
|
+
const propertyIndex = objectExpression.properties.indexOf(property);
|
|
64
|
+
if (propertyIndex < objectExpression.properties.length - 1) {
|
|
65
|
+
// The property is not the last one, so we can
|
|
66
|
+
// remove everything up to the next property
|
|
67
|
+
// which will remove the comma after it.
|
|
68
|
+
rangeToRemove = [
|
|
69
|
+
property.range[0],
|
|
70
|
+
objectExpression.properties[propertyIndex + 1].range[0],
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// The property is the last one. If the object has a
|
|
75
|
+
// trailing comma, then we want to keep the trailing comma.
|
|
76
|
+
// The simplest way to do that is to remove the property
|
|
77
|
+
// and the comma that precedes it.
|
|
78
|
+
const tokenBefore = context.sourceCode.getTokenBefore(property);
|
|
79
|
+
if (tokenBefore && utils_2.ASTUtils.isCommaToken(tokenBefore)) {
|
|
80
|
+
rangeToRemove = [tokenBefore.range[0], rangeToRemove[1]];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (aliasName === propertyName) {
|
|
87
|
+
context.report({
|
|
88
|
+
node,
|
|
89
|
+
messageId: 'noOutputRename',
|
|
90
|
+
fix: (fixer) => fixer.removeRange(rangeToRemove),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) {
|
|
94
|
+
context.report({
|
|
95
|
+
node,
|
|
96
|
+
messageId: 'noOutputRename',
|
|
97
|
+
suggest: [
|
|
98
|
+
{
|
|
99
|
+
messageId: 'suggestRemoveAliasName',
|
|
100
|
+
fix: (fixer) => fixer.removeRange(rangeToRemove),
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
messageId: 'suggestReplaceOriginalNameWithAliasName',
|
|
104
|
+
fix: (fixer) => [
|
|
105
|
+
fixer.removeRange(rangeToRemove),
|
|
106
|
+
fixer.replaceText(propertyOrMethodDefinition.key, aliasName.includes('-') ? `'${aliasName}'` : aliasName),
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
[utils_1.Selectors.OUTPUTS_METADATA_PROPERTY_LITERAL](node) {
|
|
114
|
+
const ancestorMaybeHostDirectiveAPI = node.parent?.parent?.parent?.parent?.parent;
|
|
115
|
+
if (ancestorMaybeHostDirectiveAPI &&
|
|
116
|
+
utils_1.ASTUtils.isProperty(ancestorMaybeHostDirectiveAPI)) {
|
|
117
|
+
/**
|
|
118
|
+
* Angular v15 introduced the directive composition API: https://angular.dev/guide/directives/directive-composition-api
|
|
119
|
+
* Renaming host directive outputs using this API is not a bad practice and should not be reported
|
|
120
|
+
*/
|
|
121
|
+
const hostDirectiveAPIPropertyName = 'hostDirectives';
|
|
122
|
+
if ((utils_1.ASTUtils.isLiteral(ancestorMaybeHostDirectiveAPI.key) &&
|
|
123
|
+
ancestorMaybeHostDirectiveAPI.key.value ===
|
|
124
|
+
hostDirectiveAPIPropertyName) ||
|
|
125
|
+
(utils_2.ASTUtils.isIdentifier(ancestorMaybeHostDirectiveAPI.key) &&
|
|
126
|
+
ancestorMaybeHostDirectiveAPI.key.name ===
|
|
127
|
+
hostDirectiveAPIPropertyName)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const [propertyName, aliasName] = (0, utils_1.withoutBracketsAndWhitespaces)(utils_1.ASTUtils.getRawText(node)).split(':');
|
|
132
|
+
if (!aliasName)
|
|
133
|
+
return;
|
|
134
|
+
if (aliasName === propertyName) {
|
|
135
|
+
context.report({
|
|
136
|
+
node,
|
|
137
|
+
messageId: 'noOutputRename',
|
|
138
|
+
fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, propertyName)),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) {
|
|
142
|
+
context.report({
|
|
143
|
+
node,
|
|
144
|
+
messageId: 'noOutputRename',
|
|
145
|
+
suggest: [
|
|
146
|
+
['suggestRemoveAliasName', propertyName],
|
|
147
|
+
['suggestReplaceOriginalNameWithAliasName', aliasName],
|
|
148
|
+
].map(([messageId, name]) => ({
|
|
149
|
+
messageId,
|
|
150
|
+
fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, name)),
|
|
151
|
+
})),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
'ClassDeclaration:exit'() {
|
|
156
|
+
selectors = new Set();
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
function composedName(selector, propertyName) {
|
|
162
|
+
return `${selector}${(0, utils_1.capitalize)(propertyName)}`;
|
|
163
|
+
}
|
|
164
|
+
function isAliasNameAllowed(selectors, propertyName, aliasName) {
|
|
165
|
+
return [...selectors].some((selector) => {
|
|
166
|
+
return (selector === aliasName ||
|
|
167
|
+
composedName(selector, propertyName) === aliasName);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
171
|
+
rationale: "Renaming outputs creates a confusing situation where the output has one name inside the component class (private name) and a different name in templates (public name). For example, with the @Output() decorator: '@Output(\"userDeleted\") delete = new EventEmitter()' means you call 'this.delete.emit()' in the component but bind with '(userDeleted)' in templates. Similarly, with the output() function: 'userDeleted = output({ alias: \"delete\" })' means you call 'this.userDeleted.emit()' internally but bind with '(delete)' 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.",
|
|
172
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noOutputsMetadataProperty';
|
|
3
|
+
export declare const RULE_NAME = "no-outputs-metadata-property";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noOutputsMetadataProperty", [], 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-outputs-metadata-property.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-outputs-metadata-property.d.ts","sourceRoot":"","sources":["../../src/rules/no-outputs-metadata-property.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,2BAA2B,CAAC;AACrD,eAAO,MAAM,SAAS,iCAAiC,CAAC;;;;AAGxD,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-outputs-metadata-property';
|
|
8
|
+
const METADATA_PROPERTY_NAME = 'outputs';
|
|
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
|
+
noOutputsMetadataProperty: `Use \`@Output\` 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 outputs 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: 'noOutputsMetadataProperty',
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
52
|
+
rationale: "Using the 'outputs' metadata property (@Component({ outputs: ['change'] })) is discouraged in favor of modern alternatives like the output() function (introduced in Angular v17.3) or the @Output() decorator. Modern approaches offer several benefits: (1) they make it immediately clear which properties are event emitters 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) output() uses OutputEmitterRef which provides better type safety and a more modern API than EventEmitter, and (5) @Output() decorator remains valid for EventEmitter-based outputs. 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 public API. For new code, prefer output() for its improved type safety or @Output() for traditional EventEmitter-based outputs.",
|
|
53
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noPipeImpure' | 'suggestRemovePipeImpure';
|
|
3
|
+
export declare const RULE_NAME = "no-pipe-impure";
|
|
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-pipe-impure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-pipe-impure.d.ts","sourceRoot":"","sources":["../../src/rules/no-pipe-impure.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,yBAAyB,CAAC;AACpE,eAAO,MAAM,SAAS,mBAAmB,CAAC;;;;AAE1C,wBAuCG;AAEH,eAAO,MAAM,mBAAmB;;CAE/B,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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-pipe-impure';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Disallows the declaration of impure pipes',
|
|
13
|
+
},
|
|
14
|
+
hasSuggestions: true,
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
noPipeImpure: 'Impure pipes should be avoided because they are invoked on each change-detection cycle',
|
|
18
|
+
suggestRemovePipeImpure: 'Remove `pure` property',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
const sourceCode = context.sourceCode;
|
|
24
|
+
return {
|
|
25
|
+
[`${utils_1.Selectors.PIPE_CLASS_DECORATOR} ${utils_1.Selectors.metadataProperty('pure')}:matches([value.value=false], [value.operator='!'][value.argument.value=true])`](node) {
|
|
26
|
+
context.report({
|
|
27
|
+
node,
|
|
28
|
+
messageId: 'noPipeImpure',
|
|
29
|
+
suggest: [
|
|
30
|
+
{
|
|
31
|
+
messageId: 'suggestRemovePipeImpure',
|
|
32
|
+
fix: (fixer) => utils_1.RuleFixes.getNodeToCommaRemoveFix(sourceCode, node, fixer),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
41
|
+
rationale: `Impure pipes are executed on every change detection cycle, regardless of whether their inputs have changed. In a typical Angular application, change detection runs very frequently (on every user interaction, timer, HTTP request, etc.). An impure pipe in a template with a list of 100 items might execute 100 times per change detection cycle, potentially thousands of times per second, causing severe performance problems. Pure pipes (the default) only re-execute when their input references change, making them much more efficient. Impure pipes should be avoided unless you have a specific need and understand the performance implications. In most cases, you can achieve the same result with a pure pipe and proper change detection strategy.`,
|
|
42
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'noQueriesMetadataProperty';
|
|
3
|
+
export declare const RULE_NAME = "no-queries-metadata-property";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"noQueriesMetadataProperty", [], 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-queries-metadata-property.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-queries-metadata-property.d.ts","sourceRoot":"","sources":["../../src/rules/no-queries-metadata-property.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,2BAA2B,CAAC;AACrD,eAAO,MAAM,SAAS,iCAAiC,CAAC;;;;AAIxD,wBAiCG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
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-queries-metadata-property';
|
|
7
|
+
const METADATA_PROPERTY_NAME = 'queries';
|
|
8
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
9
|
+
name: exports.RULE_NAME,
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: `Disallows usage of the \`${METADATA_PROPERTY_NAME}\` metadata property.`,
|
|
14
|
+
},
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
noQueriesMetadataProperty: `Use @${utils_1.ASTUtils.AngularInnerClassDecorators.Output} rather than the \`${METADATA_PROPERTY_NAME}\` metadata property`,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
[utils_1.Selectors.COMPONENT_OR_DIRECTIVE_CLASS_DECORATOR](node) {
|
|
24
|
+
const propertyExpression = utils_1.ASTUtils.getDecoratorPropertyValue(node, METADATA_PROPERTY_NAME);
|
|
25
|
+
if (!propertyExpression) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
context.report({
|
|
29
|
+
node: propertyExpression.parent,
|
|
30
|
+
messageId: 'noQueriesMetadataProperty',
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
37
|
+
rationale: "Using the 'queries' metadata property (@Component({ queries: { child: new ViewChild('ref') } })) is discouraged in favor of decorator syntax (@ViewChild('ref')) because: (1) decorators make it immediately clear which properties are view/content queries when reading the class, (2) decorators allow configuration right next to the property declaration, (3) decorator syntax works better with IDE features and TypeScript type checking, and (4) the decorator approach is the standard modern Angular pattern. The metadata property approach is a legacy pattern that makes code harder to read and maintain.",
|
|
38
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
export type Options = [];
|
|
3
|
+
export type MessageIds = 'noUncalledSignals' | 'suggestCallSignal';
|
|
4
|
+
export declare const RULE_NAME = "no-uncalled-signals";
|
|
5
|
+
declare const _default: ESLintUtils.RuleModule<MessageIds, [], import("../utils/create-eslint-rule").RuleDocs, ESLintUtils.RuleListener> & {
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
export default _default;
|
|
9
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
10
|
+
rationale: string;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=no-uncalled-signals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-uncalled-signals.d.ts","sourceRoot":"","sources":["../../src/rules/no-uncalled-signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAIlC,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AACnE,eAAO,MAAM,SAAS,wBAAwB,CAAC;;;;AAmB/C,wBAiFG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
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("@typescript-eslint/utils");
|
|
5
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
const signals_1 = require("../utils/signals");
|
|
7
|
+
exports.RULE_NAME = 'no-uncalled-signals';
|
|
8
|
+
const CONDITIONAL_SELECTOR = [
|
|
9
|
+
utils_1.AST_NODE_TYPES.ConditionalExpression,
|
|
10
|
+
utils_1.AST_NODE_TYPES.DoWhileStatement,
|
|
11
|
+
utils_1.AST_NODE_TYPES.ForStatement,
|
|
12
|
+
utils_1.AST_NODE_TYPES.IfStatement,
|
|
13
|
+
utils_1.AST_NODE_TYPES.SwitchCase,
|
|
14
|
+
utils_1.AST_NODE_TYPES.WhileStatement,
|
|
15
|
+
].join(',');
|
|
16
|
+
const FUNCTION_MEMBER_EXPRESSION_SELECTOR = `MemberExpression[property.type=Identifier]:matches(${[
|
|
17
|
+
'arguments',
|
|
18
|
+
'caller',
|
|
19
|
+
'length',
|
|
20
|
+
'name',
|
|
21
|
+
'toString',
|
|
22
|
+
].map((member) => `[property.name=${member}]`)})`;
|
|
23
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
24
|
+
name: exports.RULE_NAME,
|
|
25
|
+
meta: {
|
|
26
|
+
type: 'suggestion',
|
|
27
|
+
docs: {
|
|
28
|
+
description: "Warns user about unintentionally doing logic on the signal, rather than the signal's value",
|
|
29
|
+
},
|
|
30
|
+
hasSuggestions: true,
|
|
31
|
+
schema: [],
|
|
32
|
+
messages: {
|
|
33
|
+
noUncalledSignals: 'Doing logic operations on signals will give unexpected results, you probably want to invoke the signal to get its value',
|
|
34
|
+
suggestCallSignal: 'Call this signal to get its value.',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
defaultOptions: [],
|
|
38
|
+
create(context) {
|
|
39
|
+
const services = utils_1.ESLintUtils.getParserServices(context);
|
|
40
|
+
function checkForUncalledSignal(node) {
|
|
41
|
+
const type = services.getTypeAtLocation(node);
|
|
42
|
+
const symbol = type.getSymbol();
|
|
43
|
+
let isSignal = symbol && signals_1.KNOWN_SIGNAL_TYPES.has(symbol.name);
|
|
44
|
+
// If the type is not a known signal type, but it has an alias
|
|
45
|
+
// symbol, then check if that alias symbol is a known signal type.
|
|
46
|
+
// The `Signal` type will fall under this category, because it is
|
|
47
|
+
// defined as a type alias. Other signal types like `InputSignal`
|
|
48
|
+
// won't match here, because they are defined as interfaces.
|
|
49
|
+
if (!isSignal && type.aliasSymbol) {
|
|
50
|
+
isSignal = signals_1.KNOWN_SIGNAL_TYPES.has(type.aliasSymbol.name);
|
|
51
|
+
}
|
|
52
|
+
if (isSignal) {
|
|
53
|
+
context.report({
|
|
54
|
+
node,
|
|
55
|
+
messageId: 'noUncalledSignals',
|
|
56
|
+
suggest: [
|
|
57
|
+
{
|
|
58
|
+
messageId: 'suggestCallSignal',
|
|
59
|
+
fix: (fixer) => fixer.insertTextAfter(node, '()'),
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
[CONDITIONAL_SELECTOR](node) {
|
|
67
|
+
if (node.test) {
|
|
68
|
+
checkForUncalledSignal(node.test);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
UnaryExpression(node) {
|
|
72
|
+
if (node.operator !== 'delete') {
|
|
73
|
+
checkForUncalledSignal(node.argument);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
LogicalExpression(node) {
|
|
77
|
+
checkForUncalledSignal(node.left);
|
|
78
|
+
checkForUncalledSignal(node.right);
|
|
79
|
+
},
|
|
80
|
+
BinaryExpression(node) {
|
|
81
|
+
checkForUncalledSignal(node.left);
|
|
82
|
+
checkForUncalledSignal(node.right);
|
|
83
|
+
},
|
|
84
|
+
[FUNCTION_MEMBER_EXPRESSION_SELECTOR](node) {
|
|
85
|
+
checkForUncalledSignal(node.object);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
91
|
+
rationale: "Angular signals are functions that must be called to retrieve their value. A common mistake, especially for developers new to signals, is to use the signal itself in conditional or logical expressions without calling it first. For example, 'if (mySignal)' checks if the signal function exists (which is always true), not whether the signal's value is truthy. You need 'if (mySignal())' to check the value. This bug is easy to make because signals look like regular properties but behave like functions. The mistake leads to logic errors where conditions always evaluate incorrectly. This rule catches these mistakes by detecting when signals are used in conditionals, comparisons, or logical operations without being called.",
|
|
92
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type Options = [
|
|
2
|
+
{
|
|
3
|
+
prefixes: string[];
|
|
4
|
+
}
|
|
5
|
+
];
|
|
6
|
+
export type MessageIds = 'pipePrefix' | 'selectorAfterPrefixFailure';
|
|
7
|
+
export declare const RULE_NAME = "pipe-prefix";
|
|
8
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, Options, import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
9
|
+
name: string;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
12
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
13
|
+
rationale: string;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=pipe-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe-prefix.d.ts","sourceRoot":"","sources":["../../src/rules/pipe-prefix.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB;CACF,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,4BAA4B,CAAC;AACrE,eAAO,MAAM,SAAS,gBAAgB,CAAC;;;;AAEvC,wBAoGG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
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 = 'pipe-prefix';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Enforce consistent prefix for pipes.',
|
|
13
|
+
},
|
|
14
|
+
schema: [
|
|
15
|
+
{
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
prefixes: {
|
|
19
|
+
type: 'array',
|
|
20
|
+
items: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
},
|
|
23
|
+
uniqueItems: true,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
messages: {
|
|
30
|
+
pipePrefix: '@Pipes should be prefixed with {{prefixes}}',
|
|
31
|
+
selectorAfterPrefixFailure: '@Pipes should have a selector after the {{prefixes}} prefix',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultOptions: [
|
|
35
|
+
{
|
|
36
|
+
prefixes: [],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
create(context, [{ prefixes }]) {
|
|
40
|
+
function checkValidOption(prefixes) {
|
|
41
|
+
return Array.isArray(prefixes) && prefixes.length > 0;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
[utils_1.Selectors.PIPE_CLASS_DECORATOR](node) {
|
|
45
|
+
const nameSelector = utils_1.ASTUtils.getDecoratorPropertyValue(node, 'name');
|
|
46
|
+
if (!nameSelector) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const isValidOption = checkValidOption(prefixes);
|
|
50
|
+
if (!isValidOption) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const allowPrefixesExpression = prefixes.join('|');
|
|
54
|
+
const prefixValidator = utils_1.SelectorUtils.SelectorValidator.prefix(allowPrefixesExpression, utils_1.ASTUtils.OPTION_STYLE_CAMEL_CASE);
|
|
55
|
+
const selectorAfterPrefixValidator = utils_1.SelectorUtils.SelectorValidator.selectorAfterPrefix(allowPrefixesExpression);
|
|
56
|
+
let nameValue;
|
|
57
|
+
if (utils_1.ASTUtils.isStringLiteral(nameSelector)) {
|
|
58
|
+
nameValue = nameSelector.value;
|
|
59
|
+
}
|
|
60
|
+
else if (utils_1.ASTUtils.isTemplateLiteral(nameSelector) &&
|
|
61
|
+
nameSelector.quasis[0]) {
|
|
62
|
+
nameValue = nameSelector.quasis[0].value.raw;
|
|
63
|
+
}
|
|
64
|
+
if (!nameValue) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (!prefixValidator.apply(this, [nameValue])) {
|
|
68
|
+
context.report({
|
|
69
|
+
node: nameSelector,
|
|
70
|
+
messageId: 'pipePrefix',
|
|
71
|
+
data: {
|
|
72
|
+
prefixes: (0, utils_1.toHumanReadableText)(prefixes),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!selectorAfterPrefixValidator.apply(this, [nameValue])) {
|
|
78
|
+
context.report({
|
|
79
|
+
node: nameSelector,
|
|
80
|
+
messageId: 'selectorAfterPrefixFailure',
|
|
81
|
+
data: {
|
|
82
|
+
prefixes: (0, utils_1.toHumanReadableText)(prefixes),
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
91
|
+
rationale: "Prefixing pipe names helps prevent naming collisions between pipes from different libraries or modules, and makes it clear which pipes belong to your application versus third-party libraries. For example, prefixing with 'app' creates pipe names like 'appCurrency' instead of just 'currency', avoiding conflicts with Angular's built-in pipes.",
|
|
92
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type MessageIds = 'preferHostMetadataPropertyForBinding' | 'preferHostMetadataPropertyForListener';
|
|
2
|
+
export declare const RULE_NAME = "prefer-host-metadata-property";
|
|
3
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<MessageIds, [], import("../utils/create-eslint-rule").RuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
export default _default;
|
|
7
|
+
export declare const RULE_DOCS_EXTENSION: {
|
|
8
|
+
rationale: string;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=prefer-host-metadata-property.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer-host-metadata-property.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-host-metadata-property.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,GAClB,sCAAsC,GACtC,uCAAuC,CAAC;AAC5C,eAAO,MAAM,SAAS,kCAAkC,CAAC;;;;AAEzD,wBAiCG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
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 = 'prefer-host-metadata-property';
|
|
7
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
8
|
+
name: exports.RULE_NAME,
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'suggestion',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Use `host` metadata property instead of `@HostBinding` and `HostListener`',
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
preferHostMetadataPropertyForBinding: 'Prefer using the `host` metadata property in the `@Component` decorator for host bindings instead of the `@HostBinding` decorator.',
|
|
17
|
+
preferHostMetadataPropertyForListener: 'Prefer using the `host` metadata property in the `@Component` decorator for host listeners instead of the `@HostListener` decorator.',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
[utils_1.Selectors.HOST_BINDING_DECORATOR]: (node) => {
|
|
24
|
+
context.report({
|
|
25
|
+
node,
|
|
26
|
+
messageId: 'preferHostMetadataPropertyForBinding',
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
[utils_1.Selectors.HOST_LISTENER_DECORATOR]: (node) => {
|
|
30
|
+
context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: 'preferHostMetadataPropertyForListener',
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
exports.RULE_DOCS_EXTENSION = {
|
|
39
|
+
rationale: 'Using the host metadata property in the @Component or @Directive decorator instead of separate @HostBinding and @HostListener decorators consolidates all host-related bindings in one place, making it easier to see all DOM interactions at a glance. The host property approach is generally more concise and keeps component metadata organized. With decorators, host bindings and listeners are scattered throughout the class on individual properties and methods, requiring developers to hunt through the file to understand all host interactions. The centralized host property makes this information immediately visible in the decorator metadata. This is particularly helpful during code reviews and when onboarding new team members who need to understand how a component interacts with its host element. Prior to Angular v20, the Angular team actually recommended using the decorators, partly because the decorators had better IDE support for typechecking. That is no longer the case and the Angular documentation clearly states that the host metadata should be used and that the decorators are purely still there for backwards compatibility.',
|
|
40
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Options = [];
|
|
2
|
+
export type MessageIds = 'preferInject';
|
|
3
|
+
export declare const RULE_NAME = "prefer-inject";
|
|
4
|
+
declare const _default: import("@typescript-eslint/utils/ts-eslint").RuleModule<"preferInject", [], 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=prefer-inject.d.ts.map
|