eslint-config-angular-strict 2.3.83 → 2.3.85

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/README.md CHANGED
@@ -24,6 +24,7 @@ A production-ready, opinionated ESLint configuration that enforces best practice
24
24
  📘 **TypeScript**: Promise-async, type imports, strict typing, type safety, extraneous classes,...<br>
25
25
  ✨ **Code Quality**: Complexity max, file length control, import cycles detection, 100+ Unicorn best practices,...<br>
26
26
  🎨 **Style**: Airbnb extended, max line length, object/class newlines, sorted classes/imports/objects/types,...<br>
27
+ 📁 **Naming**: File/folder/class suffix conventions enforced (components, services, pipes, interfaces,...)<br>
27
28
  🔍 **Templates**: 30+ rules with alphabetical attrs, complexity max, control flow, trackBy, a11y, no-any,...
28
29
 
29
30
  ## What's Included
@@ -1,13 +1,25 @@
1
- // Class suffix enforcement (regex matched against class name)
2
- const classSuffixRestriction = (suffix, location) => ({
3
- selector: `ClassDeclaration[id.name!=/${suffix}$/]`,
4
- message: `Classes in ${location} must end with "${suffix}"`,
5
- });
1
+ // Class suffix enforcement based on Angular decorator
2
+ const decoratorClassSuffixRestrictions = [
3
+ {
4
+ selector: 'ClassDeclaration:has(> Decorator[expression.callee.name="Injectable"]):not([id.name=/Service$/])',
5
+ message: '@Injectable classes must end with "Service"',
6
+ },
7
+ {
8
+ selector: 'ClassDeclaration:has(> Decorator[expression.callee.name="Pipe"]):not([id.name=/Pipe$/])',
9
+ message: '@Pipe classes must end with "Pipe"',
10
+ },
11
+ ];
6
12
 
7
- // Files containing only imports + export const should be named *.constant.ts
13
+ // Class suffix enforcement for files (used in helpers override since undecorated)
14
+ const helpersClassSuffixRestriction = {
15
+ selector: 'ClassDeclaration[id.name!=/Helpers$/]',
16
+ message: 'Classes in *.helpers.ts must end with "Helpers"',
17
+ };
18
+
19
+ // Files containing only imports + export const (literal values, not functions) should be named *.constant.ts
8
20
  const constantsFileRestriction = {
9
21
  selector:
10
- 'Program:has(ExportNamedDeclaration VariableDeclaration[kind="const"]):not(:has(:matches(ClassDeclaration, FunctionDeclaration, TSInterfaceDeclaration, TSEnumDeclaration, TSTypeAliasDeclaration)))',
22
+ 'Program:has(ExportNamedDeclaration VariableDeclaration[kind="const"]):not(:has(:matches(ClassDeclaration, FunctionDeclaration, TSInterfaceDeclaration, TSEnumDeclaration, TSTypeAliasDeclaration))):not(:has(VariableDeclarator > :matches(ArrowFunctionExpression, FunctionExpression, CallExpression)))',
11
23
  message: 'Files containing only constants must be named constants/*.constant.ts',
12
24
  };
13
25
 
@@ -26,7 +38,7 @@ const decoratorFileRestrictions = {
26
38
  Component: {
27
39
  selector: 'Decorator[expression.callee.name="Component"]',
28
40
  message:
29
- '@Component classes must live in app.ts / components/**/*.component.ts / components/drawers/*.drawer.ts / components/modals/*.modal.ts / pages/**/*.page.ts',
41
+ '@Component classes must live in app.ts / components/**/*.component.ts / pages/**/*.component.ts / components/drawers/*.drawer.ts / components/modals/*.modal.ts / pages/**/*.page.ts',
30
42
  },
31
43
  Directive: { selector: 'Decorator[expression.callee.name="Directive"]', message: '@Directive classes must live in directives/*.directive.ts' },
32
44
  Injectable: { selector: 'Decorator[expression.callee.name="Injectable"]', message: '@Injectable classes must live in services/*.service.ts' },
@@ -47,6 +59,7 @@ export const noRestrictedSyntaxRule = [
47
59
  decoratorFileRestrictions.Directive,
48
60
  decoratorFileRestrictions.Injectable,
49
61
  decoratorFileRestrictions.Pipe,
62
+ ...decoratorClassSuffixRestrictions,
50
63
  helperFileRestriction,
51
64
  ...declarationFileRestrictions,
52
65
  ];
@@ -55,13 +68,21 @@ export const noRestrictedSyntaxRule = [
55
68
  export const namingConventionOverrides = [
56
69
  // app.ts / components/**/*.component.ts / components/drawers/*.drawer.ts / components/modals/*.modal.ts / pages/**/*.page.ts: @Component allowed
57
70
  {
58
- files: ['**/app.ts', '**/components/**/*.component.ts', '**/components/drawers/**/*.drawer.ts', '**/components/modals/**/*.modal.ts', '**/pages/**/*.page.ts'],
71
+ files: [
72
+ '**/app.ts',
73
+ '**/components/**/*.component.ts',
74
+ '**/pages/**/*.component.ts',
75
+ '**/components/drawers/**/*.drawer.ts',
76
+ '**/components/modals/**/*.modal.ts',
77
+ '**/pages/**/*.page.ts',
78
+ ],
59
79
  rules: {
60
80
  'no-restricted-syntax': [
61
81
  'error',
62
82
  decoratorFileRestrictions.Directive,
63
83
  decoratorFileRestrictions.Injectable,
64
84
  decoratorFileRestrictions.Pipe,
85
+ ...decoratorClassSuffixRestrictions,
65
86
  helperFileRestriction,
66
87
  ...declarationFileRestrictions,
67
88
  ],
@@ -77,38 +98,39 @@ export const namingConventionOverrides = [
77
98
  decoratorFileRestrictions.Component,
78
99
  decoratorFileRestrictions.Injectable,
79
100
  decoratorFileRestrictions.Pipe,
101
+ ...decoratorClassSuffixRestrictions,
80
102
  helperFileRestriction,
81
103
  ...declarationFileRestrictions,
82
104
  ],
83
105
  },
84
106
  },
85
107
 
86
- // pipes/*.pipe.ts: @Pipe allowed, class must end with "Pipe"
108
+ // pipes/*.pipe.ts: @Pipe allowed
87
109
  {
88
110
  files: ['**/pipes/**/*.pipe.ts'],
89
111
  rules: {
90
112
  'no-restricted-syntax': [
91
113
  'error',
92
- classSuffixRestriction('Pipe', '*.pipe.ts'),
93
114
  decoratorFileRestrictions.Component,
94
115
  decoratorFileRestrictions.Directive,
95
116
  decoratorFileRestrictions.Injectable,
117
+ ...decoratorClassSuffixRestrictions,
96
118
  helperFileRestriction,
97
119
  ...declarationFileRestrictions,
98
120
  ],
99
121
  },
100
122
  },
101
123
 
102
- // services/*.service.ts: @Injectable allowed, class must end with "Service"
124
+ // services/*.service.ts: @Injectable allowed
103
125
  {
104
126
  files: ['**/services/**/*.service.ts'],
105
127
  rules: {
106
128
  'no-restricted-syntax': [
107
129
  'error',
108
- classSuffixRestriction('Service', '*.service.ts'),
109
130
  decoratorFileRestrictions.Component,
110
131
  decoratorFileRestrictions.Directive,
111
132
  decoratorFileRestrictions.Pipe,
133
+ ...decoratorClassSuffixRestrictions,
112
134
  helperFileRestriction,
113
135
  ...declarationFileRestrictions,
114
136
  ],
@@ -121,11 +143,12 @@ export const namingConventionOverrides = [
121
143
  rules: {
122
144
  'no-restricted-syntax': [
123
145
  'error',
124
- classSuffixRestriction('Helpers', '*.helpers.ts'),
146
+ helpersClassSuffixRestriction,
125
147
  decoratorFileRestrictions.Component,
126
148
  decoratorFileRestrictions.Directive,
127
149
  decoratorFileRestrictions.Injectable,
128
150
  decoratorFileRestrictions.Pipe,
151
+ ...decoratorClassSuffixRestrictions,
129
152
  ...declarationFileRestrictions,
130
153
  ],
131
154
  },
@@ -242,4 +265,18 @@ export const namingConventionOverrides = [
242
265
  ],
243
266
  },
244
267
  },
268
+
269
+ // environments/*.ts: Angular environment files (only imports + export const allowed, no naming enforcement)
270
+ {
271
+ files: ['**/environments/*.ts'],
272
+ rules: {
273
+ 'no-restricted-syntax': [
274
+ 'error',
275
+ {
276
+ selector: 'Program > :not(ImportDeclaration, ExportNamedDeclaration:has(> VariableDeclaration[kind="const"]))',
277
+ message: 'Only imports and export const allowed in environments files',
278
+ },
279
+ ],
280
+ },
281
+ },
245
282
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-config-angular-strict",
3
- "version": "2.3.83",
3
+ "version": "2.3.85",
4
4
  "description": "Modern ESLint configuration with strict rules for Angular development.",
5
5
  "keywords": [
6
6
  "angular",