@angular-eslint/eslint-plugin 15.2.2-alpha.9 → 16.0.0-alpha.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.
Files changed (78) hide show
  1. package/dist/configs/README.md +9 -24
  2. package/dist/configs/all.json +2 -1
  3. package/dist/configs/recommended.json +2 -1
  4. package/dist/index.js +101 -1
  5. package/dist/rules/component-class-suffix.js +55 -0
  6. package/dist/rules/component-max-inline-declarations.js +102 -0
  7. package/dist/rules/component-selector.js +124 -0
  8. package/dist/rules/contextual-decorator.js +60 -0
  9. package/dist/rules/contextual-lifecycle.js +58 -0
  10. package/dist/rules/directive-class-suffix.js +60 -0
  11. package/dist/rules/directive-selector.js +89 -0
  12. package/dist/rules/no-attribute-decorator.js +30 -0
  13. package/dist/rules/no-conflicting-lifecycle.js +71 -0
  14. package/dist/rules/no-empty-lifecycle-method.js +66 -0
  15. package/dist/rules/no-forward-ref.js +31 -0
  16. package/dist/rules/no-host-metadata-property.js +68 -0
  17. package/dist/rules/no-input-prefix.js +105 -0
  18. package/dist/rules/no-input-rename.js +175 -0
  19. package/dist/rules/no-inputs-metadata-property.js +52 -0
  20. package/dist/rules/no-lifecycle-call.js +47 -0
  21. package/dist/rules/no-output-native.js +44 -0
  22. package/dist/rules/no-output-on-prefix.js +45 -0
  23. package/dist/rules/no-output-rename.js +107 -0
  24. package/dist/rules/no-outputs-metadata-property.js +33 -0
  25. package/dist/rules/no-pipe-impure.js +40 -0
  26. package/dist/rules/no-queries-metadata-property.js +37 -0
  27. package/dist/rules/pipe-prefix.js +79 -0
  28. package/dist/rules/prefer-on-push-component-change-detection.js +79 -0
  29. package/dist/rules/prefer-output-readonly.js +39 -0
  30. package/dist/rules/relative-url-prefix.js +55 -0
  31. package/dist/rules/require-localize-metadata.js +74 -0
  32. package/dist/rules/sort-ngmodule-metadata-arrays.js +63 -0
  33. package/dist/rules/use-component-selector.js +37 -0
  34. package/dist/rules/use-component-view-encapsulation.js +48 -0
  35. package/dist/rules/use-injectable-provided-in.js +62 -0
  36. package/dist/rules/use-lifecycle-interface.js +45 -0
  37. package/dist/rules/use-pipe-transform-interface.js +47 -0
  38. package/dist/utils/create-eslint-rule.js +26 -0
  39. package/package.json +3 -3
  40. package/dist/configs/base.json +0 -4
  41. package/dist/configs/ng-cli-compat--formatting-add-on.json +0 -51
  42. package/dist/configs/ng-cli-compat.json +0 -188
  43. package/dist/configs/recommended--extra.json +0 -40
  44. package/dist/eslint-plugin/src/index.d.ts +0 -309
  45. package/dist/eslint-plugin/src/rules/component-class-suffix.d.ts +0 -9
  46. package/dist/eslint-plugin/src/rules/component-max-inline-declarations.d.ts +0 -11
  47. package/dist/eslint-plugin/src/rules/component-selector.d.ts +0 -5
  48. package/dist/eslint-plugin/src/rules/contextual-decorator.d.ts +0 -5
  49. package/dist/eslint-plugin/src/rules/contextual-lifecycle.d.ts +0 -4
  50. package/dist/eslint-plugin/src/rules/directive-class-suffix.d.ts +0 -7
  51. package/dist/eslint-plugin/src/rules/directive-selector.d.ts +0 -5
  52. package/dist/eslint-plugin/src/rules/no-attribute-decorator.d.ts +0 -4
  53. package/dist/eslint-plugin/src/rules/no-conflicting-lifecycle.d.ts +0 -4
  54. package/dist/eslint-plugin/src/rules/no-empty-lifecycle-method.d.ts +0 -4
  55. package/dist/eslint-plugin/src/rules/no-forward-ref.d.ts +0 -5
  56. package/dist/eslint-plugin/src/rules/no-host-metadata-property.d.ts +0 -7
  57. package/dist/eslint-plugin/src/rules/no-input-prefix.d.ts +0 -7
  58. package/dist/eslint-plugin/src/rules/no-input-rename.d.ts +0 -7
  59. package/dist/eslint-plugin/src/rules/no-inputs-metadata-property.d.ts +0 -4
  60. package/dist/eslint-plugin/src/rules/no-lifecycle-call.d.ts +0 -4
  61. package/dist/eslint-plugin/src/rules/no-output-native.d.ts +0 -4
  62. package/dist/eslint-plugin/src/rules/no-output-on-prefix.d.ts +0 -4
  63. package/dist/eslint-plugin/src/rules/no-output-rename.d.ts +0 -4
  64. package/dist/eslint-plugin/src/rules/no-outputs-metadata-property.d.ts +0 -4
  65. package/dist/eslint-plugin/src/rules/no-pipe-impure.d.ts +0 -4
  66. package/dist/eslint-plugin/src/rules/no-queries-metadata-property.d.ts +0 -4
  67. package/dist/eslint-plugin/src/rules/pipe-prefix.d.ts +0 -9
  68. package/dist/eslint-plugin/src/rules/prefer-on-push-component-change-detection.d.ts +0 -4
  69. package/dist/eslint-plugin/src/rules/prefer-output-readonly.d.ts +0 -4
  70. package/dist/eslint-plugin/src/rules/relative-url-prefix.d.ts +0 -4
  71. package/dist/eslint-plugin/src/rules/require-localize-metadata.d.ts +0 -10
  72. package/dist/eslint-plugin/src/rules/sort-ngmodule-metadata-arrays.d.ts +0 -9
  73. package/dist/eslint-plugin/src/rules/use-component-selector.d.ts +0 -4
  74. package/dist/eslint-plugin/src/rules/use-component-view-encapsulation.d.ts +0 -4
  75. package/dist/eslint-plugin/src/rules/use-injectable-provided-in.d.ts +0 -7
  76. package/dist/eslint-plugin/src/rules/use-lifecycle-interface.d.ts +0 -4
  77. package/dist/eslint-plugin/src/rules/use-pipe-transform-interface.d.ts +0 -4
  78. package/dist/eslint-plugin/src/utils/create-eslint-rule.d.ts +0 -2
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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.io/guide/styleguide#style-05-13';
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: 'error',
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
+ let isAliasMetadataProperty = false;
62
+ if (node.parent && utils_1.ASTUtils.isProperty(node.parent)) {
63
+ if (utils_1.ASTUtils.getRawText(node.parent.key) !== 'alias') {
64
+ // We're within an Input decorator metadata object, but it is not the alias property
65
+ return;
66
+ }
67
+ isAliasMetadataProperty = true;
68
+ }
69
+ const aliasName = utils_1.ASTUtils.getRawText(node);
70
+ const propertyName = utils_1.ASTUtils.getRawText(propertyOrMethodDefinition.key);
71
+ if (allowedNames.includes(aliasName) ||
72
+ (ariaAttributeKeys.has(aliasName) &&
73
+ propertyName === (0, utils_1.kebabToCamelCase)(aliasName))) {
74
+ return;
75
+ }
76
+ if (aliasName === propertyName) {
77
+ context.report({
78
+ node,
79
+ messageId: 'noInputRename',
80
+ fix: (fixer) => {
81
+ if (node.parent && isAliasMetadataProperty) {
82
+ return fixer.remove(node.parent);
83
+ }
84
+ return fixer.remove(node);
85
+ },
86
+ });
87
+ }
88
+ else if (!isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName)) {
89
+ context.report({
90
+ node,
91
+ messageId: 'noInputRename',
92
+ suggest: [
93
+ {
94
+ messageId: 'suggestRemoveAliasName',
95
+ fix: (fixer) => {
96
+ if (node.parent && isAliasMetadataProperty) {
97
+ return fixer.remove(node.parent);
98
+ }
99
+ return fixer.remove(node);
100
+ },
101
+ },
102
+ {
103
+ messageId: 'suggestReplaceOriginalNameWithAliasName',
104
+ fix: (fixer) => [
105
+ fixer.remove(node),
106
+ fixer.replaceText(propertyOrMethodDefinition.key, aliasName.includes('-') ? `'${aliasName}'` : aliasName),
107
+ ],
108
+ },
109
+ ],
110
+ });
111
+ }
112
+ },
113
+ [utils_1.Selectors.INPUTS_METADATA_PROPERTY_LITERAL](node) {
114
+ var _a, _b, _c, _d;
115
+ const ancestorMaybeHostDirectiveAPI = (_d = (_c = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent) === null || _c === void 0 ? void 0 : _c.parent) === null || _d === void 0 ? void 0 : _d.parent;
116
+ if (ancestorMaybeHostDirectiveAPI &&
117
+ utils_1.ASTUtils.isProperty(ancestorMaybeHostDirectiveAPI)) {
118
+ /**
119
+ * Angular v15 introduced the directive composition API: https://angular.io/guide/directive-composition-api
120
+ * Renaming host directive inputs using this API is not a bad practice and should not be reported
121
+ */
122
+ const hostDirectiveAPIPropertyName = 'hostDirectives';
123
+ if ((utils_1.ASTUtils.isLiteral(ancestorMaybeHostDirectiveAPI.key) &&
124
+ ancestorMaybeHostDirectiveAPI.key.value ===
125
+ hostDirectiveAPIPropertyName) ||
126
+ (utils_2.ASTUtils.isIdentifier(ancestorMaybeHostDirectiveAPI.key) &&
127
+ ancestorMaybeHostDirectiveAPI.key.name ===
128
+ hostDirectiveAPIPropertyName)) {
129
+ return;
130
+ }
131
+ }
132
+ const [propertyName, aliasName] = (0, utils_1.withoutBracketsAndWhitespaces)(utils_1.ASTUtils.getRawText(node)).split(':');
133
+ if (!aliasName ||
134
+ allowedNames.includes(aliasName) ||
135
+ (ariaAttributeKeys.has(aliasName) &&
136
+ propertyName === (0, utils_1.kebabToCamelCase)(aliasName))) {
137
+ return;
138
+ }
139
+ if (aliasName === propertyName) {
140
+ context.report({
141
+ node,
142
+ messageId: 'noInputRename',
143
+ fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, propertyName)),
144
+ });
145
+ }
146
+ else if (!isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName)) {
147
+ context.report({
148
+ node,
149
+ messageId: 'noInputRename',
150
+ suggest: [
151
+ ['suggestRemoveAliasName', propertyName],
152
+ ['suggestReplaceOriginalNameWithAliasName', aliasName],
153
+ ].map(([messageId, name]) => ({
154
+ messageId,
155
+ fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, name)),
156
+ })),
157
+ });
158
+ }
159
+ },
160
+ 'ClassDeclaration:exit'() {
161
+ selectors = new Set();
162
+ },
163
+ };
164
+ },
165
+ });
166
+ function composedName(selector, propertyName) {
167
+ return `${selector}${(0, utils_1.capitalize)(propertyName)}`;
168
+ }
169
+ function isAliasNameAllowed(selectors, propertyName, aliasName, selectorDirectiveName) {
170
+ return [...selectors].some((selector) => {
171
+ return (selector === aliasName ||
172
+ selectorDirectiveName === aliasName ||
173
+ composedName(selector, propertyName) === aliasName);
174
+ });
175
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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
+ const STYLE_GUIDE_LINK = 'https://angular.io/styleguide#style-05-12';
10
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
11
+ name: exports.RULE_NAME,
12
+ meta: {
13
+ type: 'suggestion',
14
+ docs: {
15
+ description: `Disallows usage of the \`${METADATA_PROPERTY_NAME}\` metadata property. See more at ${STYLE_GUIDE_LINK}`,
16
+ recommended: 'error',
17
+ },
18
+ schema: [],
19
+ messages: {
20
+ noInputsMetadataProperty: `Use \`@Input\` rather than the \`${METADATA_PROPERTY_NAME}\` metadata property (${STYLE_GUIDE_LINK})`,
21
+ },
22
+ },
23
+ defaultOptions: [],
24
+ create(context) {
25
+ return {
26
+ [`${utils_1.Selectors.COMPONENT_OR_DIRECTIVE_CLASS_DECORATOR} ${utils_1.Selectors.metadataProperty(METADATA_PROPERTY_NAME)}`](node) {
27
+ var _a, _b;
28
+ /**
29
+ * Angular v15 introduced the directive composition API: https://angular.io/guide/directive-composition-api
30
+ * Using host directive inputs using this API is not a bad practice and should not be reported
31
+ */
32
+ const ancestorMayBeHostDirectiveAPI = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent;
33
+ if (ancestorMayBeHostDirectiveAPI &&
34
+ utils_1.ASTUtils.isProperty(ancestorMayBeHostDirectiveAPI)) {
35
+ const hostDirectiveAPIPropertyName = 'hostDirectives';
36
+ if ((utils_1.ASTUtils.isLiteral(ancestorMayBeHostDirectiveAPI.key) &&
37
+ ancestorMayBeHostDirectiveAPI.key.value ===
38
+ hostDirectiveAPIPropertyName) ||
39
+ (utils_2.ASTUtils.isIdentifier(ancestorMayBeHostDirectiveAPI.key) &&
40
+ ancestorMayBeHostDirectiveAPI.key.name ===
41
+ hostDirectiveAPIPropertyName)) {
42
+ return;
43
+ }
44
+ }
45
+ context.report({
46
+ node,
47
+ messageId: 'noInputsMetadataProperty',
48
+ });
49
+ },
50
+ };
51
+ },
52
+ });
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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
+ recommended: false,
15
+ },
16
+ schema: [],
17
+ messages: {
18
+ noLifecycleCall: 'Avoid explicit calls to lifecycle methods',
19
+ },
20
+ },
21
+ defaultOptions: [],
22
+ create(context) {
23
+ const angularLifeCycleMethodsPattern = (0, utils_1.toPattern)([
24
+ ...utils_1.ASTUtils.ANGULAR_LIFECYCLE_METHODS,
25
+ ]);
26
+ return {
27
+ [`ClassDeclaration CallExpression > MemberExpression[property.name=${angularLifeCycleMethodsPattern}]`]: (node) => {
28
+ const classDeclaration = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isClassDeclaration);
29
+ if (!classDeclaration ||
30
+ !utils_1.ASTUtils.getAngularClassDecorator(classDeclaration) ||
31
+ (utils_1.ASTUtils.isSuper(node.object) && isSuperCallAllowed(node))) {
32
+ return;
33
+ }
34
+ context.report({ node: node.parent, messageId: 'noLifecycleCall' });
35
+ },
36
+ };
37
+ },
38
+ });
39
+ function hasSameName({ property }, { key }) {
40
+ return (utils_2.ASTUtils.isIdentifier(property) &&
41
+ utils_2.ASTUtils.isIdentifier(key) &&
42
+ property.name === key.name);
43
+ }
44
+ function isSuperCallAllowed(node) {
45
+ const methodDefinition = utils_1.ASTUtils.getNearestNodeFrom(node, utils_1.ASTUtils.isMethodDefinition);
46
+ return Boolean(methodDefinition && hasSameName(node, methodDefinition));
47
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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: 'error',
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
+ });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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.io/guide/styleguide#style-05-16';
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: 'error',
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
+ });
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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.io/guide/styleguide#style-05-13';
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: 'error',
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
+ if (aliasName === propertyName) {
42
+ context.report({
43
+ node,
44
+ messageId: 'noOutputRename',
45
+ fix: (fixer) => fixer.remove(node),
46
+ });
47
+ }
48
+ else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) {
49
+ context.report({
50
+ node,
51
+ messageId: 'noOutputRename',
52
+ suggest: [
53
+ {
54
+ messageId: 'suggestRemoveAliasName',
55
+ fix: (fixer) => fixer.remove(node),
56
+ },
57
+ {
58
+ messageId: 'suggestReplaceOriginalNameWithAliasName',
59
+ fix: (fixer) => [
60
+ fixer.remove(node),
61
+ fixer.replaceText(propertyOrMethodDefinition.key, aliasName.includes('-') ? `'${aliasName}'` : aliasName),
62
+ ],
63
+ },
64
+ ],
65
+ });
66
+ }
67
+ },
68
+ [utils_1.Selectors.OUTPUTS_METADATA_PROPERTY_LITERAL](node) {
69
+ const [propertyName, aliasName] = (0, utils_1.withoutBracketsAndWhitespaces)(utils_1.ASTUtils.getRawText(node)).split(':');
70
+ if (!aliasName)
71
+ return;
72
+ if (aliasName === propertyName) {
73
+ context.report({
74
+ node,
75
+ messageId: 'noOutputRename',
76
+ fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, propertyName)),
77
+ });
78
+ }
79
+ else if (!isAliasNameAllowed(selectors, propertyName, aliasName)) {
80
+ context.report({
81
+ node,
82
+ messageId: 'noOutputRename',
83
+ suggest: [
84
+ ['suggestRemoveAliasName', propertyName],
85
+ ['suggestReplaceOriginalNameWithAliasName', aliasName],
86
+ ].map(([messageId, name]) => ({
87
+ messageId,
88
+ fix: (fixer) => fixer.replaceText(node, utils_1.ASTUtils.getReplacementText(node, name)),
89
+ })),
90
+ });
91
+ }
92
+ },
93
+ 'ClassDeclaration:exit'() {
94
+ selectors = new Set();
95
+ },
96
+ };
97
+ },
98
+ });
99
+ function composedName(selector, propertyName) {
100
+ return `${selector}${(0, utils_1.capitalize)(propertyName)}`;
101
+ }
102
+ function isAliasNameAllowed(selectors, propertyName, aliasName) {
103
+ return [...selectors].some((selector) => {
104
+ return (selector === aliasName ||
105
+ composedName(selector, propertyName) === aliasName);
106
+ });
107
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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-outputs-metadata-property';
7
+ const METADATA_PROPERTY_NAME = 'outputs';
8
+ const STYLE_GUIDE_LINK = 'https://angular.io/styleguide#style-05-12';
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. See more at ${STYLE_GUIDE_LINK}`,
15
+ recommended: 'error',
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ noOutputsMetadataProperty: `Use \`@Output\` rather than the \`${METADATA_PROPERTY_NAME}\` metadata property (${STYLE_GUIDE_LINK})`,
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
+ context.report({
27
+ node,
28
+ messageId: 'noOutputsMetadataProperty',
29
+ });
30
+ },
31
+ };
32
+ },
33
+ });
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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
+ recommended: false,
14
+ },
15
+ hasSuggestions: true,
16
+ schema: [],
17
+ messages: {
18
+ noPipeImpure: 'Impure pipes should be avoided because they are invoked on each change-detection cycle',
19
+ suggestRemovePipeImpure: 'Remove `pure` property',
20
+ },
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ const sourceCode = context.getSourceCode();
25
+ return {
26
+ [`${utils_1.Selectors.PIPE_CLASS_DECORATOR} ${utils_1.Selectors.metadataProperty('pure')}:matches([value.value=false], [value.operator='!'][value.argument.value=true])`](node) {
27
+ context.report({
28
+ node,
29
+ messageId: 'noPipeImpure',
30
+ suggest: [
31
+ {
32
+ messageId: 'suggestRemovePipeImpure',
33
+ fix: (fixer) => utils_1.RuleFixes.getNodeToCommaRemoveFix(sourceCode, node, fixer),
34
+ },
35
+ ],
36
+ });
37
+ },
38
+ };
39
+ },
40
+ });
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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
+ const STYLE_GUIDE_LINK = 'https://angular.io/styleguide#style-05-12';
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. See more at ${STYLE_GUIDE_LINK}.`,
15
+ recommended: false,
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ noQueriesMetadataProperty: `Use @${utils_1.ASTUtils.AngularInnerClassDecorators.Output} rather than the \`${METADATA_PROPERTY_NAME}\` metadata property (${STYLE_GUIDE_LINK})`,
20
+ },
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ return {
25
+ [utils_1.Selectors.COMPONENT_OR_DIRECTIVE_CLASS_DECORATOR](node) {
26
+ const propertyExpression = utils_1.ASTUtils.getDecoratorPropertyValue(node, METADATA_PROPERTY_NAME);
27
+ if (!propertyExpression) {
28
+ return;
29
+ }
30
+ context.report({
31
+ node: propertyExpression.parent,
32
+ messageId: 'noQueriesMetadataProperty',
33
+ });
34
+ },
35
+ };
36
+ },
37
+ });
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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
+ recommended: false,
14
+ },
15
+ schema: [
16
+ {
17
+ type: 'object',
18
+ properties: {
19
+ prefixes: {
20
+ type: 'array',
21
+ items: {
22
+ type: 'string',
23
+ },
24
+ minimum: 1,
25
+ uniqueItems: true,
26
+ },
27
+ },
28
+ additionalProperties: false,
29
+ },
30
+ ],
31
+ messages: {
32
+ pipePrefix: '@Pipes should be prefixed by {{prefixes}}',
33
+ },
34
+ },
35
+ defaultOptions: [
36
+ {
37
+ prefixes: [],
38
+ },
39
+ ],
40
+ create(context, [{ prefixes }]) {
41
+ function checkValidOption(prefixes) {
42
+ return Array.isArray(prefixes) && prefixes.length > 0;
43
+ }
44
+ return {
45
+ [utils_1.Selectors.PIPE_CLASS_DECORATOR](node) {
46
+ const nameSelector = utils_1.ASTUtils.getDecoratorPropertyValue(node, 'name');
47
+ if (!nameSelector) {
48
+ return;
49
+ }
50
+ const isValidOption = checkValidOption(prefixes);
51
+ if (!isValidOption) {
52
+ return;
53
+ }
54
+ const allowPrefixesExpression = prefixes.join('|');
55
+ const prefixValidator = utils_1.SelectorUtils.SelectorValidator.prefix(allowPrefixesExpression, 'camelCase');
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
+ }
76
+ },
77
+ };
78
+ },
79
+ });