@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,58 @@
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 = 'contextual-lifecycle';
7
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
8
+ name: exports.RULE_NAME,
9
+ meta: {
10
+ type: 'problem',
11
+ docs: {
12
+ description: 'Ensures that lifecycle methods are used in a correct context',
13
+ recommended: 'error',
14
+ },
15
+ schema: [],
16
+ messages: {
17
+ contextualLifecycle: `Angular will not invoke the \`{{methodName}}\` lifecycle method within \`@{{classDecoratorName}}()\` classes`,
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ function checkContext({ parent }, decorator) {
23
+ const classDeclaration = parent;
24
+ const allowedMethods = utils_1.ASTUtils.ANGULAR_CLASS_DECORATOR_LIFECYCLE_METHOD_MAPPER.get(decorator);
25
+ const declaredMethods = utils_1.ASTUtils.getDeclaredMethods(classDeclaration);
26
+ for (const method of declaredMethods) {
27
+ const methodName = utils_1.ASTUtils.getMethodName(method);
28
+ if (!methodName ||
29
+ !utils_1.ASTUtils.isAngularLifecycleMethod(methodName) ||
30
+ (allowedMethods === null || allowedMethods === void 0 ? void 0 : allowedMethods.has(methodName))) {
31
+ continue;
32
+ }
33
+ context.report({
34
+ node: method.key,
35
+ messageId: 'contextualLifecycle',
36
+ data: { classDecoratorName: decorator, methodName },
37
+ });
38
+ }
39
+ }
40
+ return {
41
+ [utils_1.Selectors.COMPONENT_CLASS_DECORATOR](node) {
42
+ checkContext(node, utils_1.ASTUtils.AngularClassDecorators.Component);
43
+ },
44
+ [utils_1.Selectors.DIRECTIVE_CLASS_DECORATOR](node) {
45
+ checkContext(node, utils_1.ASTUtils.AngularClassDecorators.Directive);
46
+ },
47
+ [utils_1.Selectors.INJECTABLE_CLASS_DECORATOR](node) {
48
+ checkContext(node, utils_1.ASTUtils.AngularClassDecorators.Injectable);
49
+ },
50
+ [utils_1.Selectors.MODULE_CLASS_DECORATOR](node) {
51
+ checkContext(node, utils_1.ASTUtils.AngularClassDecorators.NgModule);
52
+ },
53
+ [utils_1.Selectors.PIPE_CLASS_DECORATOR](node) {
54
+ checkContext(node, utils_1.ASTUtils.AngularClassDecorators.Pipe);
55
+ },
56
+ };
57
+ },
58
+ });
@@ -0,0 +1,60 @@
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 = 'directive-class-suffix';
7
+ const STYLE_GUIDE_LINK = 'https://angular.io/styleguide#style-02-03';
8
+ const DEFAULT_SUFFIXES = ['Directive'];
9
+ const VALIDATOR_SUFFIX = 'Validator';
10
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
11
+ name: exports.RULE_NAME,
12
+ meta: {
13
+ type: 'suggestion',
14
+ docs: {
15
+ description: `Classes decorated with @Directive must have suffix "Directive" (or custom) in their name. See more at ${STYLE_GUIDE_LINK}`,
16
+ recommended: 'error',
17
+ },
18
+ schema: [
19
+ {
20
+ type: 'object',
21
+ properties: {
22
+ suffixes: {
23
+ type: 'array',
24
+ items: {
25
+ type: 'string',
26
+ },
27
+ },
28
+ },
29
+ additionalProperties: false,
30
+ },
31
+ ],
32
+ messages: {
33
+ directiveClassSuffix: `Directive class names should end with one of these suffixes: {{suffixes}} (${STYLE_GUIDE_LINK})`,
34
+ },
35
+ },
36
+ defaultOptions: [{ suffixes: DEFAULT_SUFFIXES }],
37
+ create(context, [{ suffixes }]) {
38
+ return {
39
+ [utils_1.Selectors.DIRECTIVE_CLASS_DECORATOR](node) {
40
+ var _a;
41
+ const selectorPropertyValue = utils_1.ASTUtils.getDecoratorPropertyValue(node, 'selector');
42
+ if (!selectorPropertyValue)
43
+ return;
44
+ const classParent = node.parent;
45
+ const className = utils_1.ASTUtils.getClassName(classParent);
46
+ const declaredInterfaceNames = utils_1.ASTUtils.getDeclaredInterfaceNames(classParent);
47
+ const hasValidatorInterface = declaredInterfaceNames.some((interfaceName) => interfaceName.endsWith(VALIDATOR_SUFFIX));
48
+ const allSuffixes = suffixes.concat(hasValidatorInterface ? VALIDATOR_SUFFIX : []);
49
+ if (!className ||
50
+ !allSuffixes.some((suffix) => className.endsWith(suffix))) {
51
+ context.report({
52
+ node: (_a = classParent.id) !== null && _a !== void 0 ? _a : classParent,
53
+ messageId: 'directiveClassSuffix',
54
+ data: { suffixes: (0, utils_1.toHumanReadableText)(allSuffixes) },
55
+ });
56
+ }
57
+ },
58
+ };
59
+ },
60
+ });
@@ -0,0 +1,89 @@
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 = 'directive-selector';
7
+ const STYLE_GUIDE_PREFIX_LINK = 'https://angular.io/guide/styleguide#style-02-08';
8
+ const STYLE_GUIDE_STYLE_TYPE_LINK = 'https://angular.io/guide/styleguide#style-02-06';
9
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
10
+ name: exports.RULE_NAME,
11
+ meta: {
12
+ type: 'suggestion',
13
+ docs: {
14
+ description: `Directive selectors should follow given naming rules. See more at ${STYLE_GUIDE_STYLE_TYPE_LINK} and ${STYLE_GUIDE_PREFIX_LINK}.`,
15
+ recommended: false,
16
+ },
17
+ schema: [
18
+ {
19
+ type: 'object',
20
+ properties: {
21
+ type: {
22
+ oneOf: [
23
+ { type: 'string' },
24
+ {
25
+ type: 'array',
26
+ items: {
27
+ enum: [
28
+ utils_1.SelectorUtils.OPTION_TYPE_ELEMENT,
29
+ utils_1.SelectorUtils.OPTION_TYPE_ATTRIBUTE,
30
+ ],
31
+ },
32
+ },
33
+ ],
34
+ },
35
+ prefix: {
36
+ oneOf: [{ type: 'string' }, { type: 'array' }],
37
+ },
38
+ style: {
39
+ type: 'string',
40
+ enum: [
41
+ utils_1.ASTUtils.OPTION_STYLE_CAMEL_CASE,
42
+ utils_1.ASTUtils.OPTION_STYLE_KEBAB_CASE,
43
+ ],
44
+ },
45
+ },
46
+ additionalProperties: false,
47
+ },
48
+ ],
49
+ messages: {
50
+ prefixFailure: `The selector should start with one of these prefixes: {{prefix}} (${STYLE_GUIDE_PREFIX_LINK})`,
51
+ styleFailure: `The selector should be {{style}} (${STYLE_GUIDE_STYLE_TYPE_LINK})`,
52
+ typeFailure: `The selector should be used as an {{type}} (${STYLE_GUIDE_STYLE_TYPE_LINK})`,
53
+ },
54
+ },
55
+ defaultOptions: [
56
+ {
57
+ type: '',
58
+ prefix: '',
59
+ style: '',
60
+ },
61
+ ],
62
+ create(context, [{ type, prefix, style }]) {
63
+ return {
64
+ [utils_1.Selectors.DIRECTIVE_CLASS_DECORATOR](node) {
65
+ const rawSelectors = utils_1.ASTUtils.getDecoratorPropertyValue(node, 'selector');
66
+ if (!rawSelectors) {
67
+ return;
68
+ }
69
+ const isValidOptions = utils_1.SelectorUtils.checkValidOptions(type, prefix, style);
70
+ if (!isValidOptions) {
71
+ return;
72
+ }
73
+ const hasExpectedSelector = utils_1.SelectorUtils.checkSelector(rawSelectors, type, (0, utils_1.arrayify)(prefix), style);
74
+ if (hasExpectedSelector === null) {
75
+ return;
76
+ }
77
+ if (!hasExpectedSelector.hasExpectedType) {
78
+ utils_1.SelectorUtils.reportTypeError(rawSelectors, type, context);
79
+ }
80
+ else if (!hasExpectedSelector.hasExpectedStyle) {
81
+ utils_1.SelectorUtils.reportStyleError(rawSelectors, style, context);
82
+ }
83
+ else if (!hasExpectedSelector.hasExpectedPrefix) {
84
+ utils_1.SelectorUtils.reportPrefixError(rawSelectors, prefix, context);
85
+ }
86
+ },
87
+ };
88
+ },
89
+ });
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RULE_NAME = void 0;
4
+ const create_eslint_rule_1 = require("../utils/create-eslint-rule");
5
+ exports.RULE_NAME = 'no-attribute-decorator';
6
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
7
+ name: exports.RULE_NAME,
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: `The @Attribute decorator is used to obtain a single value for an attribute. This is a much less common use-case than getting a stream of values (using @Input), so often the @Attribute decorator is mistakenly used when @Input was what was intended. This rule disallows usage of @Attribute decorator altogether in order to prevent these mistakes.`,
12
+ recommended: false,
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ noAttributeDecorator: '@Attribute can only obtain a single value and is rarely what is required. Use @Input instead to retrieve a stream of values.',
17
+ },
18
+ },
19
+ defaultOptions: [],
20
+ create(context) {
21
+ return {
22
+ 'ClassDeclaration MethodDefinition[key.name="constructor"] Decorator[expression.callee.name="Attribute"]'(node) {
23
+ context.report({
24
+ node,
25
+ messageId: 'noAttributeDecorator',
26
+ });
27
+ },
28
+ };
29
+ },
30
+ });
@@ -0,0 +1,71 @@
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-conflicting-lifecycle';
8
+ const LIFECYCLE_INTERFACES = [
9
+ utils_1.ASTUtils.AngularLifecycleInterfaces.DoCheck,
10
+ utils_1.ASTUtils.AngularLifecycleInterfaces.OnChanges,
11
+ ];
12
+ const LIFECYCLE_METHODS = [
13
+ utils_1.ASTUtils.AngularLifecycleMethods.ngDoCheck,
14
+ utils_1.ASTUtils.AngularLifecycleMethods.ngOnChanges,
15
+ ];
16
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
17
+ name: exports.RULE_NAME,
18
+ meta: {
19
+ type: 'suggestion',
20
+ docs: {
21
+ description: 'Ensures that directives not implement conflicting lifecycle interfaces.',
22
+ recommended: false,
23
+ },
24
+ schema: [],
25
+ messages: {
26
+ noConflictingLifecycleInterface: `Implementing ${utils_1.ASTUtils.AngularLifecycleInterfaces.DoCheck} and ${utils_1.ASTUtils.AngularLifecycleInterfaces.OnChanges} in a class is not recommended`,
27
+ noConflictingLifecycleMethod: `Declaring ${utils_1.ASTUtils.AngularLifecycleMethods.ngDoCheck} and ${utils_1.ASTUtils.AngularLifecycleMethods.ngOnChanges} method in a class is not recommended`,
28
+ },
29
+ },
30
+ defaultOptions: [],
31
+ create(context) {
32
+ const validateInterfaces = (node) => {
33
+ const declaredAngularLifecycleInterfaces = utils_1.ASTUtils.getDeclaredAngularLifecycleInterfaces(node);
34
+ const hasInterfaceConflictingLifecycle = LIFECYCLE_INTERFACES.every((lifecycleInterface) => declaredAngularLifecycleInterfaces.includes(lifecycleInterface));
35
+ if (!hasInterfaceConflictingLifecycle)
36
+ return;
37
+ const declaredInterfaces = utils_1.ASTUtils.getInterfaces(node);
38
+ const declaredAngularLifecycleInterfacesNodes = declaredInterfaces.filter((node) => {
39
+ const interfaceName = utils_1.ASTUtils.getInterfaceName(node);
40
+ return (interfaceName && utils_1.ASTUtils.isAngularLifecycleInterface(interfaceName));
41
+ });
42
+ for (const interFaceNode of declaredAngularLifecycleInterfacesNodes) {
43
+ context.report({
44
+ node: interFaceNode,
45
+ messageId: 'noConflictingLifecycleInterface',
46
+ });
47
+ }
48
+ };
49
+ const validateMethods = (node) => {
50
+ const declaredAngularLifecycleMethods = utils_1.ASTUtils.getDeclaredAngularLifecycleMethods(node);
51
+ const hasMethodConflictingLifecycle = LIFECYCLE_METHODS.every((lifecycleMethod) => declaredAngularLifecycleMethods.includes(lifecycleMethod));
52
+ if (!hasMethodConflictingLifecycle)
53
+ return;
54
+ const declaredMethods = utils_1.ASTUtils.getDeclaredMethods(node);
55
+ const declaredAngularLifecycleMethodNodes = declaredMethods.filter((node) => utils_2.ASTUtils.isIdentifier(node.key) &&
56
+ utils_1.ASTUtils.isAngularLifecycleMethod(node.key.name));
57
+ for (const method of declaredAngularLifecycleMethodNodes) {
58
+ context.report({
59
+ node: method,
60
+ messageId: 'noConflictingLifecycleMethod',
61
+ });
62
+ }
63
+ };
64
+ return {
65
+ ClassDeclaration(node) {
66
+ validateInterfaces(node);
67
+ validateMethods(node);
68
+ },
69
+ };
70
+ },
71
+ });
@@ -0,0 +1,66 @@
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-empty-lifecycle-method';
7
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
8
+ name: exports.RULE_NAME,
9
+ meta: {
10
+ type: 'suggestion',
11
+ docs: {
12
+ description: 'Disallows declaring empty lifecycle methods',
13
+ recommended: 'error',
14
+ },
15
+ hasSuggestions: true,
16
+ schema: [],
17
+ messages: {
18
+ noEmptyLifecycleMethod: 'Lifecycle methods should not be empty',
19
+ suggestRemoveLifecycleMethod: 'Remove lifecycle method',
20
+ },
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ const sourceCode = context.getSourceCode();
25
+ const angularLifecycleMethodsPattern = (0, utils_1.toPattern)([
26
+ ...utils_1.ASTUtils.ANGULAR_LIFECYCLE_METHODS,
27
+ ]);
28
+ return {
29
+ [`ClassDeclaration:has(Decorator[expression.callee.name=/^(Component|Directive|Injectable|NgModule|Pipe)$/]) > ClassBody > ${utils_1.Selectors.methodDefinition(angularLifecycleMethodsPattern)}[value.body.body.length=0]`](node) {
30
+ context.report({
31
+ node,
32
+ messageId: 'noEmptyLifecycleMethod',
33
+ suggest: [
34
+ {
35
+ messageId: 'suggestRemoveLifecycleMethod',
36
+ fix: (fixer) => {
37
+ var _a;
38
+ const importDeclarations = (_a = utils_1.ASTUtils.getImportDeclarations(node, '@angular/core')) !== null && _a !== void 0 ? _a : [];
39
+ const interfaceName = utils_1.ASTUtils.getRawText(node).replace(/^ng+/, '');
40
+ const text = sourceCode.getText();
41
+ const totalInterfaceOccurrences = getTotalInterfaceOccurrences(text, interfaceName);
42
+ const totalInterfaceOccurrencesSafeForRemoval = 2;
43
+ return [
44
+ fixer.remove(node),
45
+ utils_1.RuleFixes.getImplementsRemoveFix(sourceCode, node.parent.parent, interfaceName, fixer),
46
+ totalInterfaceOccurrences <=
47
+ totalInterfaceOccurrencesSafeForRemoval
48
+ ? utils_1.RuleFixes.getImportRemoveFix(sourceCode, importDeclarations, interfaceName, fixer)
49
+ : null,
50
+ ].filter(utils_1.isNotNullOrUndefined);
51
+ },
52
+ },
53
+ ],
54
+ });
55
+ },
56
+ };
57
+ },
58
+ });
59
+ function stripSpecialCharacters(text) {
60
+ return text.replace(/[\W]/g, '');
61
+ }
62
+ function getTotalInterfaceOccurrences(text, interfaceName) {
63
+ return text
64
+ .split(' ')
65
+ .filter((item) => stripSpecialCharacters(item) === interfaceName).length;
66
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FORWARD_REF = exports.RULE_NAME = void 0;
4
+ const create_eslint_rule_1 = require("../utils/create-eslint-rule");
5
+ exports.RULE_NAME = 'no-forward-ref';
6
+ exports.FORWARD_REF = 'forwardRef';
7
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
8
+ name: exports.RULE_NAME,
9
+ meta: {
10
+ type: 'suggestion',
11
+ docs: {
12
+ description: `Disallows usage of \`${exports.FORWARD_REF}\` references for DI`,
13
+ recommended: false,
14
+ },
15
+ schema: [],
16
+ messages: {
17
+ noForwardRef: `Avoid using \`${exports.FORWARD_REF}\``,
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ return {
23
+ [`CallExpression[callee.type="Identifier"][callee.name="${exports.FORWARD_REF}"]`](node) {
24
+ context.report({
25
+ node,
26
+ messageId: 'noForwardRef',
27
+ });
28
+ },
29
+ };
30
+ },
31
+ });
@@ -0,0 +1,68 @@
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-host-metadata-property';
8
+ const DEFAULT_OPTIONS = { allowStatic: false };
9
+ const METADATA_PROPERTY_NAME = 'host';
10
+ const STYLE_GUIDE_LINK = 'https://angular.io/styleguide#style-06-03';
11
+ exports.default = (0, create_eslint_rule_1.createESLintRule)({
12
+ name: exports.RULE_NAME,
13
+ meta: {
14
+ type: 'suggestion',
15
+ docs: {
16
+ description: `Disallows usage of the \`${METADATA_PROPERTY_NAME}\` metadata property. See more at ${STYLE_GUIDE_LINK}`,
17
+ recommended: 'error',
18
+ },
19
+ schema: [
20
+ {
21
+ type: 'object',
22
+ properties: {
23
+ allowStatic: {
24
+ type: 'boolean',
25
+ default: DEFAULT_OPTIONS.allowStatic,
26
+ },
27
+ },
28
+ additionalProperties: false,
29
+ },
30
+ ],
31
+ messages: {
32
+ noHostMetadataProperty: `Use @${utils_1.ASTUtils.AngularInnerClassDecorators.HostBinding} or @${utils_1.ASTUtils.AngularInnerClassDecorators.HostListener} rather than the \`${METADATA_PROPERTY_NAME}\` metadata property (${STYLE_GUIDE_LINK})`,
33
+ },
34
+ },
35
+ defaultOptions: [DEFAULT_OPTIONS],
36
+ create(context, [{ allowStatic }]) {
37
+ return {
38
+ [`${utils_1.Selectors.COMPONENT_OR_DIRECTIVE_CLASS_DECORATOR} Property[key.name="${METADATA_PROPERTY_NAME}"]`](node) {
39
+ const properties = allowStatic && utils_1.ASTUtils.isObjectExpression(node.value)
40
+ ? node.value.properties.filter(isDynamic)
41
+ : [node];
42
+ properties.forEach((property) => {
43
+ context.report({
44
+ node: property,
45
+ messageId: 'noHostMetadataProperty',
46
+ });
47
+ });
48
+ },
49
+ };
50
+ },
51
+ });
52
+ function startsWithLetter({ [0]: firstLetter }) {
53
+ return firstLetter.toLowerCase() !== firstLetter.toUpperCase();
54
+ }
55
+ function isEmptyStringValue(property) {
56
+ return (utils_1.ASTUtils.isStringLiteral(property.value) && property.value.value === '');
57
+ }
58
+ function isStatic(property) {
59
+ return (!property.computed &&
60
+ (utils_2.ASTUtils.isIdentifier(property.key) ||
61
+ (utils_1.ASTUtils.isStringLiteral(property.key) &&
62
+ startsWithLetter(property.key.value))));
63
+ }
64
+ function isDynamic(property) {
65
+ return (utils_1.ASTUtils.isProperty(property) &&
66
+ !isStatic(property) &&
67
+ !isEmptyStringValue(property));
68
+ }
@@ -0,0 +1,105 @@
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-input-prefix';
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 input bindings, including aliases, are not named or prefixed by the configured disallowed prefixes',
13
+ recommended: false,
14
+ },
15
+ schema: [
16
+ {
17
+ type: 'object',
18
+ properties: {
19
+ prefixes: {
20
+ type: 'array',
21
+ items: {
22
+ type: 'string',
23
+ },
24
+ },
25
+ },
26
+ additionalProperties: false,
27
+ },
28
+ ],
29
+ messages: {
30
+ noInputPrefix: 'Input bindings, including aliases, should not be named, nor prefixed by {{prefixes}}',
31
+ },
32
+ },
33
+ defaultOptions: [{ prefixes: [] }],
34
+ create(context, [{ prefixes }]) {
35
+ return {
36
+ [utils_1.Selectors.INPUT_PROPERTY_OR_SETTER](node) {
37
+ const rawPropertyName = utils_1.ASTUtils.getRawText(node);
38
+ const hasDisallowedPrefix = prefixes.some((prefix) => isDisallowedPrefix(prefix, rawPropertyName));
39
+ // Direct violation on the property name
40
+ if (hasDisallowedPrefix) {
41
+ context.report({
42
+ node,
43
+ messageId: 'noInputPrefix',
44
+ data: {
45
+ prefixes: (0, utils_1.toHumanReadableText)(prefixes),
46
+ },
47
+ });
48
+ }
49
+ // Check if decorator alias has a violation
50
+ let aliasProperty;
51
+ if (!node.parent) {
52
+ return;
53
+ }
54
+ const inputDecorator = utils_1.ASTUtils.getDecorator(node.parent, 'Input');
55
+ if (inputDecorator &&
56
+ utils_1.ASTUtils.isCallExpression(inputDecorator.expression)) {
57
+ // Angular 16+ alias property syntax
58
+ aliasProperty = utils_1.ASTUtils.getDecoratorPropertyValue(inputDecorator, 'alias');
59
+ let aliasValue = '';
60
+ let aliasArg;
61
+ if (aliasProperty) {
62
+ aliasValue = utils_1.ASTUtils.getRawText(aliasProperty);
63
+ }
64
+ else if (inputDecorator.expression.arguments.length > 0 &&
65
+ (utils_1.ASTUtils.isLiteral(inputDecorator.expression.arguments[0]) ||
66
+ utils_1.ASTUtils.isTemplateLiteral(inputDecorator.expression.arguments[0]))) {
67
+ aliasArg = inputDecorator.expression.arguments[0];
68
+ aliasValue = utils_1.ASTUtils.getRawText(aliasArg);
69
+ }
70
+ const hasDisallowedPrefix = prefixes.some((prefix) => isDisallowedPrefix(prefix, aliasValue));
71
+ if (!hasDisallowedPrefix) {
72
+ return;
73
+ }
74
+ return context.report({
75
+ node: aliasProperty || aliasArg || node,
76
+ messageId: 'noInputPrefix',
77
+ data: {
78
+ prefixes: (0, utils_1.toHumanReadableText)(prefixes),
79
+ },
80
+ });
81
+ }
82
+ },
83
+ [utils_1.Selectors.INPUTS_METADATA_PROPERTY_LITERAL](node) {
84
+ const [propertyName, aliasName] = utils_1.ASTUtils.getRawText(node)
85
+ .replace(/\s/g, '')
86
+ .split(':');
87
+ const hasDisallowedPrefix = prefixes.some((prefix) => isDisallowedPrefix(prefix, propertyName, aliasName));
88
+ if (!hasDisallowedPrefix) {
89
+ return;
90
+ }
91
+ context.report({
92
+ node,
93
+ messageId: 'noInputPrefix',
94
+ data: {
95
+ prefixes: (0, utils_1.toHumanReadableText)(prefixes),
96
+ },
97
+ });
98
+ },
99
+ };
100
+ },
101
+ });
102
+ function isDisallowedPrefix(prefix, propertyName, aliasName = '') {
103
+ const prefixPattern = new RegExp(`^${prefix}(([^a-z])|(?=$))`);
104
+ return prefixPattern.test(propertyName) || prefixPattern.test(aliasName);
105
+ }