@ethlete/eslint-plugin 0.1.0-next.0 → 0.1.0-next.10

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 (54) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/package.json +6 -5
  3. package/src/configs/recommended.js +414 -0
  4. package/src/index.js +138 -0
  5. package/src/rules/angular-decorator-property-order.js +205 -0
  6. package/src/rules/class-constant-property.js +365 -0
  7. package/src/rules/class-member-order.js +456 -0
  8. package/src/rules/enforce-routing-view-naming.js +84 -0
  9. package/src/rules/guard-return-newline.js +81 -0
  10. package/src/rules/inject-member-accessibility.js +93 -0
  11. package/src/rules/internals/angular-member-visibility.js +233 -0
  12. package/src/rules/internals/implemented-contract-members.js +294 -0
  13. package/src/rules/no-angular-router-api.js +158 -0
  14. package/src/rules/no-angular-seo-services.js +82 -0
  15. package/src/rules/no-direct-dom-manipulation.js +160 -0
  16. package/src/rules/no-document-cookie.js +55 -0
  17. package/src/rules/no-dom-query.js +81 -0
  18. package/src/rules/no-empty-angular-metadata-arrays.js +104 -0
  19. package/src/rules/no-empty-newlines-between-imports.js +45 -0
  20. package/src/rules/no-inject-chain.js +44 -0
  21. package/src/rules/no-leading-underscore-class-member.js +120 -0
  22. package/src/rules/no-legacy-angular-decorators.js +405 -0
  23. package/src/rules/no-locale-id.js +57 -0
  24. package/src/rules/no-member-alias.js +151 -0
  25. package/src/rules/no-native-observers.js +102 -0
  26. package/src/rules/no-pipe-logic.js +76 -0
  27. package/src/rules/no-public-property.js +85 -0
  28. package/src/rules/no-readonly-signal.js +119 -0
  29. package/src/rules/no-redundant-internal.js +94 -0
  30. package/src/rules/no-redundant-on-push-change-detection.js +237 -0
  31. package/src/rules/no-rxjs-in-effect.js +88 -0
  32. package/src/rules/no-screaming-case-local.js +113 -0
  33. package/src/rules/no-standalone-flag.js +95 -0
  34. package/src/rules/no-subscribe-in-pipe.js +89 -0
  35. package/src/rules/no-subscribe-with-body.js +95 -0
  36. package/src/rules/no-trivial-return-type.js +104 -0
  37. package/src/rules/no-trivial-wrapper-method.js +120 -0
  38. package/src/rules/no-type-only-import.js +79 -0
  39. package/src/rules/no-typed-injected-element-ref.js +88 -0
  40. package/src/rules/no-unused-class-member.js +209 -0
  41. package/src/rules/no-window-location.js +109 -0
  42. package/src/rules/prefer-clone-equal.js +112 -0
  43. package/src/rules/prefer-concise-angular-host-directives.js +225 -0
  44. package/src/rules/prefer-concise-angular-style-metadata.js +108 -0
  45. package/src/rules/prefer-element-dimensions.js +128 -0
  46. package/src/rules/prefer-linked-signal.js +124 -0
  47. package/src/rules/prefer-match-media.js +101 -0
  48. package/src/rules/prefer-rxjs-timer.js +82 -0
  49. package/src/rules/prefer-scroll-state.js +158 -0
  50. package/src/rules/prefer-viewport-size.js +75 -0
  51. package/src/rules/require-dollar-suffix.js +123 -0
  52. package/src/rules/require-on-push-change-detection.js +320 -0
  53. package/src/rules/require-view-encapsulation-none.js +264 -0
  54. package/src/rules/template-member-accessibility.js +204 -0
@@ -0,0 +1,204 @@
1
+ // @ts-check
2
+ 'use strict';
3
+
4
+ const {
5
+ getAngularMetadata,
6
+ getMemberName,
7
+ isReferencedFromTemplateOrHost,
8
+ } = require('./internals/angular-member-visibility');
9
+ const { getImplementedContractMemberNames } = require('./internals/implemented-contract-members');
10
+
11
+ /**
12
+ * @param {any} node
13
+ */
14
+ const isInjectCall = (node) => {
15
+ if (!node || node.type !== 'PropertyDefinition' || !node.value) return false;
16
+ if (node.value.type !== 'CallExpression') return false;
17
+ return node.value.callee.type === 'Identifier' && node.value.callee.name === 'inject';
18
+ };
19
+
20
+ /**
21
+ * @param {any} node
22
+ * @param {import('eslint').SourceCode} sourceCode
23
+ */
24
+ const buildPublicFix = (node, sourceCode) => {
25
+ const keyToken = sourceCode.getFirstToken(node.key);
26
+ if (!keyToken) return null;
27
+
28
+ if (node.accessibility) {
29
+ const firstToken = sourceCode.getFirstToken(node);
30
+ if (!firstToken) return null;
31
+
32
+ return (fixer) => fixer.replaceText(firstToken, 'public');
33
+ }
34
+
35
+ return (fixer) => fixer.insertTextBefore(keyToken, 'public ');
36
+ };
37
+
38
+ /**
39
+ * @param {any} node
40
+ * @param {import('eslint').SourceCode} sourceCode
41
+ */
42
+ const buildRemoveProtectedFix = (node, sourceCode) => {
43
+ const protectedToken = sourceCode.getFirstToken(node, (token) => token.value === 'protected');
44
+ const nextToken = protectedToken ? sourceCode.getTokenAfter(protectedToken) : null;
45
+
46
+ if (!protectedToken || !nextToken) {
47
+ return null;
48
+ }
49
+
50
+ return (fixer) => fixer.removeRange([protectedToken.range[0], nextToken.range[0]]);
51
+ };
52
+
53
+ /**
54
+ * @param {any} node
55
+ */
56
+ const isSupportedMember = (node) => {
57
+ if (node.type === 'PropertyDefinition') {
58
+ return true;
59
+ }
60
+
61
+ if (node.type === 'MethodDefinition') {
62
+ return node.kind !== 'constructor';
63
+ }
64
+
65
+ return false;
66
+ };
67
+
68
+ /** @type {import('eslint').Rule.RuleModule} */
69
+ const templateMemberAccessibility = {
70
+ meta: {
71
+ type: 'problem',
72
+ docs: {
73
+ description:
74
+ 'Require non-injected Angular class members referenced from templates or host bindings to use an explicit accessibility modifier, require explicit public on implicitly public surface members, and disallow unnecessary protected on other members.',
75
+ recommended: true,
76
+ },
77
+ fixable: 'code',
78
+ schema: [],
79
+ messages: {
80
+ shouldBePublic: 'Member `{{name}}` implements a public contract and should use an explicit `public` modifier.',
81
+ shouldBeExplicitPublic:
82
+ 'Member `{{name}}` is implicitly public. Use an explicit `public` modifier. Keep or add `@internal` when it is only technically reachable.',
83
+ shouldBeExplicit:
84
+ 'Member `{{name}}` is referenced from a template or host binding. Use `public` when it is intentionally exposed outside the component; otherwise use `protected`.',
85
+ shouldNotBeProtected:
86
+ 'Member `{{name}}` should not be protected because it is not referenced from a template or host binding.',
87
+ },
88
+ },
89
+ create(context) {
90
+ const sourceCode = context.sourceCode;
91
+ /** @type {WeakMap<any, Set<string>>} */
92
+ const contractMembersCache = new WeakMap();
93
+
94
+ /**
95
+ * @param {any} classNode
96
+ */
97
+ const getContractMembers = (classNode) => {
98
+ if (contractMembersCache.has(classNode)) {
99
+ return contractMembersCache.get(classNode);
100
+ }
101
+ const result = getImplementedContractMemberNames({ classNode, context });
102
+ contractMembersCache.set(classNode, result);
103
+ return result;
104
+ };
105
+
106
+ /**
107
+ * @param {any} node
108
+ */
109
+ const checkMember = (node) => {
110
+ if (!isSupportedMember(node)) return;
111
+ if (node.type === 'PropertyDefinition' && isInjectCall(node)) return;
112
+
113
+ const classNode = node.parent && node.parent.type === 'ClassBody' ? node.parent.parent : null;
114
+ if (!classNode || (classNode.type !== 'ClassDeclaration' && classNode.type !== 'ClassExpression')) return;
115
+ if (!getAngularMetadata(classNode)) return;
116
+
117
+ const memberName = getMemberName(node);
118
+ if (!memberName) return;
119
+
120
+ const implementedContractMembers = getContractMembers(classNode);
121
+ const implementsPublicContract = implementedContractMembers.has(memberName);
122
+
123
+ if (implementsPublicContract) {
124
+ if (node.accessibility === 'public') return;
125
+
126
+ context.report({
127
+ node,
128
+ messageId: 'shouldBePublic',
129
+ data: { name: memberName },
130
+ fix: buildPublicFix(node, sourceCode),
131
+ });
132
+
133
+ return;
134
+ }
135
+
136
+ const isTemplateVisible = isReferencedFromTemplateOrHost(classNode, memberName, context);
137
+
138
+ if (isTemplateVisible) {
139
+ if (node.accessibility === 'protected' || node.accessibility === 'public') return;
140
+
141
+ context.report({
142
+ node,
143
+ messageId: 'shouldBeExplicit',
144
+ data: { name: memberName },
145
+ fix: buildPublicFix(node, sourceCode),
146
+ });
147
+
148
+ return;
149
+ }
150
+
151
+ if (node.accessibility !== 'protected') return;
152
+
153
+ context.report({
154
+ node,
155
+ messageId: 'shouldNotBeProtected',
156
+ data: { name: memberName },
157
+ fix: buildRemoveProtectedFix(node, sourceCode),
158
+ });
159
+
160
+ return;
161
+ };
162
+
163
+ /**
164
+ * @param {any} node
165
+ */
166
+ const checkImplicitPublicMember = (node) => {
167
+ if (!isSupportedMember(node)) return;
168
+ if (node.type === 'PropertyDefinition' && isInjectCall(node)) return;
169
+
170
+ const classNode = node.parent && node.parent.type === 'ClassBody' ? node.parent.parent : null;
171
+ if (!classNode || (classNode.type !== 'ClassDeclaration' && classNode.type !== 'ClassExpression')) return;
172
+ if (!getAngularMetadata(classNode)) return;
173
+
174
+ const memberName = getMemberName(node);
175
+ if (!memberName) return;
176
+
177
+ const implementedContractMembers = getContractMembers(classNode);
178
+ if (implementedContractMembers.has(memberName)) return;
179
+
180
+ if (isReferencedFromTemplateOrHost(classNode, memberName, context)) return;
181
+ if (node.accessibility) return;
182
+
183
+ context.report({
184
+ node,
185
+ messageId: 'shouldBeExplicitPublic',
186
+ data: { name: memberName },
187
+ fix: buildPublicFix(node, sourceCode),
188
+ });
189
+ };
190
+
191
+ return {
192
+ PropertyDefinition(node) {
193
+ checkMember(node);
194
+ checkImplicitPublicMember(node);
195
+ },
196
+ MethodDefinition(node) {
197
+ checkMember(node);
198
+ checkImplicitPublicMember(node);
199
+ },
200
+ };
201
+ },
202
+ };
203
+
204
+ module.exports = templateMemberAccessibility;