@ethlete/eslint-plugin 0.1.0-next.1 → 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.
- package/CHANGELOG.md +58 -0
- package/package.json +4 -2
- package/src/configs/recommended.js +63 -11
- package/src/index.js +24 -2
- package/src/rules/angular-decorator-property-order.js +3 -15
- package/src/rules/class-constant-property.js +365 -0
- package/src/rules/class-member-order.js +456 -0
- package/src/rules/inject-member-accessibility.js +5 -198
- package/src/rules/internals/angular-member-visibility.js +233 -0
- package/src/rules/internals/implemented-contract-members.js +294 -0
- package/src/rules/no-empty-angular-metadata-arrays.js +104 -0
- package/src/rules/no-empty-newlines-between-imports.js +45 -0
- package/src/rules/no-leading-underscore-class-member.js +120 -0
- package/src/rules/no-legacy-angular-decorators.js +196 -1
- package/src/rules/no-public-property.js +47 -1
- package/src/rules/no-redundant-internal.js +94 -0
- package/src/rules/no-redundant-on-push-change-detection.js +237 -0
- package/src/rules/no-standalone-flag.js +95 -0
- package/src/rules/no-typed-injected-element-ref.js +88 -0
- package/src/rules/require-on-push-change-detection.js +320 -0
- package/src/rules/require-view-encapsulation-none.js +215 -52
- package/src/rules/template-member-accessibility.js +204 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-next.10
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`4e9f2b4`](https://github.com/ethlete-io/ethdk/commit/4e9f2b4d12335fafef192350aef8ffc584996a91) Thanks [@TomTomB](https://github.com/TomTomB)! - Make the OnPush change detection rules Angular-version aware. Since OnPush is the default from Angular 22, declaring it is now redundant:
|
|
8
|
+
- Add `no-redundant-on-push-change-detection`, which flags and auto-fixes explicit `changeDetection: ChangeDetectionStrategy.OnPush` on `@Component`, and removes the now-unused `ChangeDetectionStrategy` import when it is no longer referenced. Active on Angular >= 22.
|
|
9
|
+
- `require-on-push-change-detection` now only applies on Angular <= 21, where OnPush is opt-in.
|
|
10
|
+
|
|
11
|
+
Both rules resolve the workspace's Angular major version automatically (overridable via `settings.ethlete.angularMajor`) and stay inert when they don't apply, so exactly one of them is ever active for a given Angular version.
|
|
12
|
+
|
|
13
|
+
## 0.1.0-next.9
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`b323ef6`](https://github.com/ethlete-io/ethdk/commit/b323ef66130d196e5c893e844d50ecfc85487373) Thanks [@TomTomB](https://github.com/TomTomB)! - `class-member-order`: recognize custom `injectXyz()` helper functions (not just the raw `inject()` call) as inject-group members, so they're required to be declared before inputs/outputs/queries/properties like other injected dependencies.
|
|
18
|
+
|
|
19
|
+
## 0.1.0-next.8
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- [`edea44b`](https://github.com/ethlete-io/ethdk/commit/edea44bf1c494420f02b545202f4b24db9a6395c) Thanks [@TomTomB](https://github.com/TomTomB)! - Update to angular 22
|
|
24
|
+
|
|
25
|
+
## 0.1.0-next.7
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- [`7e8ac6b`](https://github.com/ethlete-io/ethdk/commit/7e8ac6bf18f580289c7c7966c6c33a2a7f077299) Thanks [@TomTomB](https://github.com/TomTomB)! - Fix false positives in pipes
|
|
30
|
+
|
|
31
|
+
## 0.1.0-next.6
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- [`b73a127`](https://github.com/ethlete-io/ethdk/commit/b73a127002a06e3aa0c4e7e977b1ad1f3e04e7e6) Thanks [@TomTomB](https://github.com/TomTomB)! - Bump yet again, final one for sure, pinky promise
|
|
36
|
+
|
|
37
|
+
## 0.1.0-beta.5
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- [`ddb5d09`](https://github.com/ethlete-io/ethdk/commit/ddb5d09e4bc56e18cc8c228aa78a200441e7a766) Thanks [@TomTomB](https://github.com/TomTomB)! - Bump to beta
|
|
42
|
+
|
|
43
|
+
## 0.1.0-next.4
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- [`a690217`](https://github.com/ethlete-io/ethdk/commit/a6902172efd9bd1956a16237e79acbfbd816d946) Thanks [@TomTomB](https://github.com/TomTomB)! - Version bump only
|
|
48
|
+
|
|
49
|
+
## 0.1.0-next.3
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- [`5d1d3ac`](https://github.com/ethlete-io/ethdk/commit/5d1d3accbd4a7657bc50cdc0653a7ca24fe761e2) Thanks [@TomTomB](https://github.com/TomTomB)! - Fix minor lint edge cases
|
|
54
|
+
|
|
55
|
+
## 0.1.0-next.2
|
|
56
|
+
|
|
57
|
+
### Patch Changes
|
|
58
|
+
|
|
59
|
+
- [#2957](https://github.com/ethlete-io/ethdk/pull/2957) [`81173d4`](https://github.com/ethlete-io/ethdk/commit/81173d426d1a53ea2a7af0d16d6eb0fe4a16dbd9) Thanks [@github-actions](https://github.com/apps/github-actions)! - Add missing auto fixers
|
|
60
|
+
|
|
3
61
|
## 0.1.0-next.1
|
|
4
62
|
|
|
5
63
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ethlete/eslint-plugin",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"peerDependencies": {
|
|
8
8
|
"@angular-eslint/eslint-plugin-template": ">=21.0.0",
|
|
9
|
+
"@angular/core": "22.0.5",
|
|
9
10
|
"@typescript-eslint/eslint-plugin": ">=8.0.0",
|
|
10
11
|
"eslint": ">=9.0.0",
|
|
11
|
-
"tslib": "2.8.1"
|
|
12
|
+
"tslib": "2.8.1",
|
|
13
|
+
"typescript": "6.0.3"
|
|
12
14
|
}
|
|
13
15
|
}
|
|
@@ -71,10 +71,16 @@ const recommendedTs = {
|
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
selector: ['variable', 'parameter', 'property', 'parameterProperty', 'accessor'],
|
|
74
|
-
leadingUnderscore: '
|
|
74
|
+
leadingUnderscore: 'allow',
|
|
75
75
|
trailingUnderscore: 'forbid',
|
|
76
76
|
format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
|
|
77
77
|
},
|
|
78
|
+
{
|
|
79
|
+
selector: 'method',
|
|
80
|
+
leadingUnderscore: 'allow',
|
|
81
|
+
trailingUnderscore: 'forbid',
|
|
82
|
+
format: null,
|
|
83
|
+
},
|
|
78
84
|
{
|
|
79
85
|
selector: 'typeLike',
|
|
80
86
|
leadingUnderscore: 'forbid',
|
|
@@ -133,11 +139,6 @@ const recommendedTs = {
|
|
|
133
139
|
selector: 'FunctionExpression[async=true]',
|
|
134
140
|
message: 'No async/await. Use RxJS for all async operations.',
|
|
135
141
|
},
|
|
136
|
-
// No public keyword on class members
|
|
137
|
-
{
|
|
138
|
-
selector: "MethodDefinition[accessibility='public']",
|
|
139
|
-
message: 'No public keyword on class members.',
|
|
140
|
-
},
|
|
141
142
|
// No static class members
|
|
142
143
|
{
|
|
143
144
|
selector: 'MethodDefinition[static=true]',
|
|
@@ -190,6 +191,12 @@ const recommendedTs = {
|
|
|
190
191
|
message:
|
|
191
192
|
'No barrel imports (index files). Import directly from the source file to avoid breaking lazy loading.',
|
|
192
193
|
},
|
|
194
|
+
// No on-prefixed method names — name the method after what it does, not that it handles an event
|
|
195
|
+
{
|
|
196
|
+
selector: 'MethodDefinition[key.name=/^on[A-Z]/]',
|
|
197
|
+
message:
|
|
198
|
+
'No on-prefixed method names. Name the method after what it does (e.g. onLayoutChange -> syncGridItemsWithLayout).',
|
|
199
|
+
},
|
|
193
200
|
],
|
|
194
201
|
|
|
195
202
|
// ── Native DOM globals ──────────────────────────────────────────────────
|
|
@@ -212,8 +219,18 @@ const recommendedTs = {
|
|
|
212
219
|
// No inject(X).member chaining — assign to a const first
|
|
213
220
|
'ethlete/no-inject-chain': 'error',
|
|
214
221
|
|
|
215
|
-
//
|
|
216
|
-
'ethlete/no-
|
|
222
|
+
// inject(ElementRef) must use inject<ElementRef<HTMLElement>>(ElementRef) — not inject(ElementRef) or inject(ElementRef<...>)
|
|
223
|
+
'ethlete/no-typed-injected-element-ref': 'error',
|
|
224
|
+
|
|
225
|
+
// No @internal on members already hidden by private/protected
|
|
226
|
+
'ethlete/no-redundant-internal': 'error',
|
|
227
|
+
|
|
228
|
+
// Non-injected Angular members should use explicit accessibility when they
|
|
229
|
+
// participate in the reachable surface. Template/host members must be
|
|
230
|
+
// explicit, other implicit-public members must be public. @internal does
|
|
231
|
+
// not replace explicit public; protected remains valid for template-only
|
|
232
|
+
// members.
|
|
233
|
+
'ethlete/template-member-accessibility': 'error',
|
|
217
234
|
|
|
218
235
|
// No explicit trivial return types TypeScript can infer
|
|
219
236
|
'ethlete/no-trivial-return-type': 'error',
|
|
@@ -269,6 +286,9 @@ const recommendedTs = {
|
|
|
269
286
|
// No `import type { Foo }` or `import { type Foo }` — use regular value imports
|
|
270
287
|
'ethlete/no-type-only-import': 'error',
|
|
271
288
|
|
|
289
|
+
// No empty blank lines between import declarations
|
|
290
|
+
'ethlete/no-empty-newlines-between-imports': 'error',
|
|
291
|
+
|
|
272
292
|
// No legacy Angular decorators — use signal-based APIs (input, output, viewChild, etc.)
|
|
273
293
|
// and host: {} bindings (instead of @HostBinding / @HostListener)
|
|
274
294
|
'ethlete/no-legacy-angular-decorators': 'error',
|
|
@@ -304,6 +324,9 @@ const recommendedTs = {
|
|
|
304
324
|
// — widen the accessibility of the source member instead
|
|
305
325
|
'ethlete/no-member-alias': 'error',
|
|
306
326
|
|
|
327
|
+
// No leading underscores on class members; auto-fix private members when safe
|
|
328
|
+
'ethlete/no-leading-underscore-class-member': 'error',
|
|
329
|
+
|
|
307
330
|
// No declared-but-never-read private/protected class members
|
|
308
331
|
'ethlete/no-unused-class-member': 'error',
|
|
309
332
|
|
|
@@ -311,6 +334,12 @@ const recommendedTs = {
|
|
|
311
334
|
// — use viewChild() / viewChildren() or contentChild() / contentChildren() instead
|
|
312
335
|
'ethlete/no-dom-query': 'error',
|
|
313
336
|
|
|
337
|
+
// Remove empty imports: [] and hostDirectives: [] metadata noise
|
|
338
|
+
'ethlete/no-empty-angular-metadata-arrays': 'error',
|
|
339
|
+
|
|
340
|
+
// Remove standalone metadata — standalone is implicit and should not be declared
|
|
341
|
+
'ethlete/no-standalone-flag': 'error',
|
|
342
|
+
|
|
314
343
|
// ── Angular outputs ─────────────────────────────────────────────────────
|
|
315
344
|
|
|
316
345
|
// No on-prefixed outputs (onSelectDate → selectDate)
|
|
@@ -321,8 +350,13 @@ const recommendedTs = {
|
|
|
321
350
|
|
|
322
351
|
// ── Angular components ──────────────────────────────────────────────────
|
|
323
352
|
|
|
324
|
-
//
|
|
325
|
-
|
|
353
|
+
// Require ChangeDetectionStrategy.OnPush on Angular <= 21, where it is opt-in
|
|
354
|
+
// (version-aware: inert on Angular 22+, where OnPush is the default).
|
|
355
|
+
'ethlete/require-on-push-change-detection': 'error',
|
|
356
|
+
|
|
357
|
+
// No redundant ChangeDetectionStrategy.OnPush — it is the default since Angular 22
|
|
358
|
+
// (version-aware: inert on older Angular). Also removes the now-unused import.
|
|
359
|
+
'ethlete/no-redundant-on-push-change-detection': 'error',
|
|
326
360
|
|
|
327
361
|
// Always use ViewEncapsulation.None (angular-eslint's use-component-view-encapsulation does the
|
|
328
362
|
// opposite — it disallows None, so we use our own custom rule instead)
|
|
@@ -331,6 +365,12 @@ const recommendedTs = {
|
|
|
331
365
|
// Keep Angular @Component / @Directive metadata in a consistent order
|
|
332
366
|
'ethlete/angular-decorator-property-order': 'error',
|
|
333
367
|
|
|
368
|
+
// Keep class members in the documented styleguide order
|
|
369
|
+
'ethlete/class-member-order': 'error',
|
|
370
|
+
|
|
371
|
+
// True class constants should be readonly and use SCREAMING_CASE
|
|
372
|
+
'ethlete/class-constant-property': 'error',
|
|
373
|
+
|
|
334
374
|
// Prefer shorthand hostDirectives entries and keep extended host directive configs ordered
|
|
335
375
|
'ethlete/prefer-concise-angular-host-directives': 'error',
|
|
336
376
|
|
|
@@ -359,4 +399,16 @@ const recommendedTemplate = {
|
|
|
359
399
|
},
|
|
360
400
|
};
|
|
361
401
|
|
|
362
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Relaxed rules for test/spec files.
|
|
404
|
+
* Non-null assertions are common and intentional in tests.
|
|
405
|
+
* @type {import('eslint').Linter.Config}
|
|
406
|
+
*/
|
|
407
|
+
const recommendedSpec = {
|
|
408
|
+
files: ['**/*.spec.ts'],
|
|
409
|
+
rules: {
|
|
410
|
+
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
411
|
+
},
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
module.exports = { recommendedTs, recommendedTemplate, recommendedSpec };
|
package/src/index.js
CHANGED
|
@@ -22,6 +22,8 @@ const noSubscribeWithBody = require('./rules/no-subscribe-with-body');
|
|
|
22
22
|
const requireDollarSuffix = require('./rules/require-dollar-suffix');
|
|
23
23
|
const noPipeLogic = require('./rules/no-pipe-logic');
|
|
24
24
|
const enforceRoutingViewNaming = require('./rules/enforce-routing-view-naming');
|
|
25
|
+
const noRedundantOnPushChangeDetection = require('./rules/no-redundant-on-push-change-detection');
|
|
26
|
+
const requireOnPushChangeDetection = require('./rules/require-on-push-change-detection');
|
|
25
27
|
const requireViewEncapsulationNone = require('./rules/require-view-encapsulation-none');
|
|
26
28
|
const noLegacyAngularDecorators = require('./rules/no-legacy-angular-decorators');
|
|
27
29
|
const noAngularSeoServices = require('./rules/no-angular-seo-services');
|
|
@@ -29,16 +31,25 @@ const preferCloneEqual = require('./rules/prefer-clone-equal');
|
|
|
29
31
|
const noDocumentCookie = require('./rules/no-document-cookie');
|
|
30
32
|
const injectMemberAccessibility = require('./rules/inject-member-accessibility');
|
|
31
33
|
const noPublicProperty = require('./rules/no-public-property');
|
|
34
|
+
const noRedundantInternal = require('./rules/no-redundant-internal');
|
|
35
|
+
const templateMemberAccessibility = require('./rules/template-member-accessibility');
|
|
32
36
|
const noAngularRouterApi = require('./rules/no-angular-router-api');
|
|
33
37
|
const noWindowLocation = require('./rules/no-window-location');
|
|
34
38
|
const noLocaleId = require('./rules/no-locale-id');
|
|
35
39
|
const noMemberAlias = require('./rules/no-member-alias');
|
|
36
40
|
const noUnusedClassMember = require('./rules/no-unused-class-member');
|
|
37
41
|
const noDomQuery = require('./rules/no-dom-query');
|
|
42
|
+
const noLeadingUnderscoreClassMember = require('./rules/no-leading-underscore-class-member');
|
|
43
|
+
const noEmptyAngularMetadataArrays = require('./rules/no-empty-angular-metadata-arrays');
|
|
44
|
+
const noStandaloneFlag = require('./rules/no-standalone-flag');
|
|
45
|
+
const noEmptyNewlinesBetweenImports = require('./rules/no-empty-newlines-between-imports');
|
|
38
46
|
const angularDecoratorPropertyOrder = require('./rules/angular-decorator-property-order');
|
|
47
|
+
const classMemberOrder = require('./rules/class-member-order');
|
|
48
|
+
const classConstantProperty = require('./rules/class-constant-property');
|
|
39
49
|
const preferConciseAngularHostDirectives = require('./rules/prefer-concise-angular-host-directives');
|
|
40
50
|
const preferConciseAngularStyleMetadata = require('./rules/prefer-concise-angular-style-metadata');
|
|
41
|
-
const
|
|
51
|
+
const noTypedInjectedElementRef = require('./rules/no-typed-injected-element-ref');
|
|
52
|
+
const { recommendedTs, recommendedTemplate, recommendedSpec } = require('./configs/recommended');
|
|
42
53
|
|
|
43
54
|
/** @type {import('eslint').ESLint.Plugin} */
|
|
44
55
|
const plugin = {
|
|
@@ -68,6 +79,8 @@ const plugin = {
|
|
|
68
79
|
'require-dollar-suffix': requireDollarSuffix,
|
|
69
80
|
'no-pipe-logic': noPipeLogic,
|
|
70
81
|
'enforce-routing-view-naming': enforceRoutingViewNaming,
|
|
82
|
+
'no-redundant-on-push-change-detection': noRedundantOnPushChangeDetection,
|
|
83
|
+
'require-on-push-change-detection': requireOnPushChangeDetection,
|
|
71
84
|
'require-view-encapsulation-none': requireViewEncapsulationNone,
|
|
72
85
|
'no-legacy-angular-decorators': noLegacyAngularDecorators,
|
|
73
86
|
'no-angular-seo-services': noAngularSeoServices,
|
|
@@ -75,15 +88,24 @@ const plugin = {
|
|
|
75
88
|
'no-document-cookie': noDocumentCookie,
|
|
76
89
|
'inject-member-accessibility': injectMemberAccessibility,
|
|
77
90
|
'no-public-property': noPublicProperty,
|
|
91
|
+
'no-redundant-internal': noRedundantInternal,
|
|
92
|
+
'template-member-accessibility': templateMemberAccessibility,
|
|
78
93
|
'no-angular-router-api': noAngularRouterApi,
|
|
79
94
|
'no-window-location': noWindowLocation,
|
|
80
95
|
'no-locale-id': noLocaleId,
|
|
81
96
|
'no-member-alias': noMemberAlias,
|
|
82
97
|
'no-unused-class-member': noUnusedClassMember,
|
|
83
98
|
'no-dom-query': noDomQuery,
|
|
99
|
+
'no-leading-underscore-class-member': noLeadingUnderscoreClassMember,
|
|
100
|
+
'no-empty-angular-metadata-arrays': noEmptyAngularMetadataArrays,
|
|
101
|
+
'no-standalone-flag': noStandaloneFlag,
|
|
102
|
+
'no-empty-newlines-between-imports': noEmptyNewlinesBetweenImports,
|
|
84
103
|
'angular-decorator-property-order': angularDecoratorPropertyOrder,
|
|
104
|
+
'class-member-order': classMemberOrder,
|
|
105
|
+
'class-constant-property': classConstantProperty,
|
|
85
106
|
'prefer-concise-angular-host-directives': preferConciseAngularHostDirectives,
|
|
86
107
|
'prefer-concise-angular-style-metadata': preferConciseAngularStyleMetadata,
|
|
108
|
+
'no-typed-injected-element-ref': noTypedInjectedElementRef,
|
|
87
109
|
},
|
|
88
110
|
};
|
|
89
111
|
|
|
@@ -108,7 +130,7 @@ const configs = {
|
|
|
108
130
|
* Both together as a flat array — the most common usage:
|
|
109
131
|
* export default [...baseConfig, ...ethlete.configs.recommended]
|
|
110
132
|
*/
|
|
111
|
-
recommended: [recommendedTsWithPlugin, recommendedTemplateWithPlugin],
|
|
133
|
+
recommended: [recommendedTsWithPlugin, recommendedTemplateWithPlugin, recommendedSpec],
|
|
112
134
|
};
|
|
113
135
|
|
|
114
136
|
const ethletePlugin = { ...plugin, configs };
|
|
@@ -29,28 +29,16 @@ const DECORATOR_CONFIG = {
|
|
|
29
29
|
'outputs',
|
|
30
30
|
'queries',
|
|
31
31
|
'preserveWhitespaces',
|
|
32
|
-
'standalone',
|
|
33
32
|
'schemas',
|
|
34
33
|
'jit',
|
|
35
34
|
'exportAs',
|
|
36
35
|
],
|
|
37
36
|
orderText:
|
|
38
|
-
'selector, template/templateUrl, styleUrl/styleUrls, encapsulation, changeDetection, imports, providers, viewProviders, animations, hostDirectives, host, styles, inputs, outputs, queries, preserveWhitespaces,
|
|
37
|
+
'selector, template/templateUrl, styleUrl/styleUrls, encapsulation, changeDetection, imports, providers, viewProviders, animations, hostDirectives, host, styles, inputs, outputs, queries, preserveWhitespaces, schemas, jit, exportAs',
|
|
39
38
|
},
|
|
40
39
|
Directive: {
|
|
41
|
-
order: [
|
|
42
|
-
|
|
43
|
-
'exportAs',
|
|
44
|
-
'providers',
|
|
45
|
-
'inputs',
|
|
46
|
-
'outputs',
|
|
47
|
-
'queries',
|
|
48
|
-
'hostDirectives',
|
|
49
|
-
'host',
|
|
50
|
-
'standalone',
|
|
51
|
-
'jit',
|
|
52
|
-
],
|
|
53
|
-
orderText: 'selector, exportAs, providers, inputs, outputs, queries, hostDirectives, host, standalone, jit',
|
|
40
|
+
order: ['selector', 'exportAs', 'providers', 'inputs', 'outputs', 'queries', 'hostDirectives', 'host', 'jit'],
|
|
41
|
+
orderText: 'selector, exportAs, providers, inputs, outputs, queries, hostDirectives, host, jit',
|
|
54
42
|
},
|
|
55
43
|
};
|
|
56
44
|
|