@taiga-ui/eslint-plugin-experience-next 0.480.0 → 0.482.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.
- package/README.md +50 -1676
- package/index.d.ts +59 -21
- package/index.esm.js +3135 -3029
- package/package.json +1 -1
- package/rules/attrs-newline.d.ts +3 -1
- package/rules/element-newline.d.ts +3 -1
- package/rules/no-duplicate-attrs.d.ts +3 -1
- package/rules/no-duplicate-id.d.ts +3 -1
- package/rules/no-duplicate-in-head.d.ts +3 -1
- package/rules/no-obsolete-attrs.d.ts +3 -1
- package/rules/no-obsolete-tags.d.ts +3 -1
- package/rules/quotes.d.ts +3 -1
- package/rules/recommended/decorator-key-sort.d.ts +5 -0
- package/rules/{host-attributes-sort.d.ts → recommended/host-attributes-sort.d.ts} +2 -2
- package/rules/recommended/html-logical-properties.d.ts +5 -0
- package/rules/recommended/injection-token-description.d.ts +4 -0
- package/rules/recommended/no-commonjs-import-patterns.d.ts +6 -0
- package/rules/recommended/no-deep-imports-to-indexed-packages.d.ts +5 -0
- package/rules/recommended/no-deep-imports.d.ts +10 -0
- package/rules/recommended/no-href-with-router-link.d.ts +5 -0
- package/rules/recommended/no-implicit-public.d.ts +4 -0
- package/rules/recommended/no-import-assertions.d.ts +4 -0
- package/rules/recommended/no-infinite-loop.d.ts +5 -0
- package/rules/recommended/no-legacy-peer-deps.d.ts +4 -0
- package/rules/recommended/no-playwright-empty-fill.d.ts +4 -0
- package/rules/recommended/no-project-as-in-ng-template.d.ts +5 -0
- package/rules/recommended/no-redundant-type-annotation.d.ts +9 -0
- package/rules/recommended/no-side-effects-in-computed.d.ts +4 -0
- package/rules/recommended/object-single-line.d.ts +7 -0
- package/rules/recommended/prefer-combined-if-control-flow.d.ts +5 -0
- package/rules/recommended/prefer-multi-arg-push.d.ts +4 -0
- package/rules/recommended/prefer-namespace-keyword.d.ts +5 -0
- package/rules/{short-tui-imports.d.ts → recommended/short-tui-imports.d.ts} +1 -2
- package/rules/{single-line-class-property-spacing.d.ts → recommended/single-line-class-property-spacing.d.ts} +1 -2
- package/rules/recommended/standalone-imports-sort.d.ts +7 -0
- package/rules/require-doctype.d.ts +3 -1
- package/rules/require-img-alt.d.ts +3 -1
- package/rules/require-lang.d.ts +3 -1
- package/rules/require-li-container.d.ts +3 -1
- package/rules/require-title.d.ts +3 -1
- package/rules/taiga-specific/array-as-const.d.ts +5 -0
- package/rules/taiga-specific/class-property-naming.d.ts +9 -0
- package/rules/taiga-specific/flat-exports.d.ts +4 -0
- package/rules/taiga-specific/no-restricted-attr-values.d.ts +5 -0
- package/rules/{prefer-deep-imports.d.ts → taiga-specific/prefer-deep-imports.d.ts} +2 -2
- package/rules/{strict-tui-doc-example.d.ts → taiga-specific/strict-tui-doc-example.d.ts} +1 -2
- package/rules/utils/angular/untracked-docs.d.ts +2 -5
- package/rules/utils/create-rule.d.ts +13 -0
- package/rules/array-as-const.d.ts +0 -3
- package/rules/class-property-naming.d.ts +0 -10
- package/rules/decorator-key-sort.d.ts +0 -3
- package/rules/flat-exports.d.ts +0 -5
- package/rules/html-logical-properties.d.ts +0 -3
- package/rules/injection-token-description.d.ts +0 -5
- package/rules/no-commonjs-import-patterns.d.ts +0 -6
- package/rules/no-deep-imports-to-indexed-packages.d.ts +0 -5
- package/rules/no-deep-imports.d.ts +0 -11
- package/rules/no-href-with-router-link.d.ts +0 -3
- package/rules/no-implicit-public.d.ts +0 -5
- package/rules/no-import-assertions.d.ts +0 -5
- package/rules/no-infinite-loop.d.ts +0 -6
- package/rules/no-legacy-peer-deps.d.ts +0 -5
- package/rules/no-playwright-empty-fill.d.ts +0 -5
- package/rules/no-project-as-in-ng-template.d.ts +0 -3
- package/rules/no-redundant-type-annotation.d.ts +0 -10
- package/rules/no-restricted-attr-values.d.ts +0 -4
- package/rules/no-side-effects-in-computed.d.ts +0 -5
- package/rules/object-single-line.d.ts +0 -8
- package/rules/prefer-combined-if-control-flow.d.ts +0 -5
- package/rules/prefer-multi-arg-push.d.ts +0 -5
- package/rules/prefer-namespace-keyword.d.ts +0 -5
- package/rules/standalone-imports-sort.d.ts +0 -8
- /package/rules/{no-fully-untracked-effect.d.ts → recommended/no-fully-untracked-effect.d.ts} +0 -0
- /package/rules/{no-signal-reads-after-await-in-reactive-context.d.ts → recommended/no-signal-reads-after-await-in-reactive-context.d.ts} +0 -0
- /package/rules/{no-string-literal-concat.d.ts → recommended/no-string-literal-concat.d.ts} +0 -0
- /package/rules/{no-untracked-outside-reactive-context.d.ts → recommended/no-untracked-outside-reactive-context.d.ts} +0 -0
- /package/rules/{no-useless-untracked.d.ts → recommended/no-useless-untracked.d.ts} +0 -0
- /package/rules/{prefer-untracked-incidental-signal-reads.d.ts → recommended/prefer-untracked-incidental-signal-reads.d.ts} +0 -0
- /package/rules/{prefer-untracked-signal-getter.d.ts → recommended/prefer-untracked-signal-getter.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -37,1679 +37,53 @@ export default [
|
|
|
37
37
|
- 🔧 = fixable
|
|
38
38
|
- 💡 = has suggestions
|
|
39
39
|
|
|
40
|
-
| Rule
|
|
41
|
-
|
|
|
42
|
-
| array-as-const
|
|
43
|
-
| attrs-newline
|
|
44
|
-
| class-property-naming
|
|
45
|
-
| decorator-key-sort
|
|
46
|
-
| element-newline
|
|
47
|
-
| flat-exports
|
|
48
|
-
| host-attributes-sort
|
|
49
|
-
| injection-token-description
|
|
50
|
-
| no-commonjs-import-patterns
|
|
51
|
-
| no-deep-imports
|
|
52
|
-
| no-deep-imports-to-indexed-packages
|
|
53
|
-
| no-duplicate-attrs
|
|
54
|
-
| no-duplicate-id
|
|
55
|
-
| no-duplicate-in-head
|
|
56
|
-
| no-fully-untracked-effect
|
|
57
|
-
| no-href-with-router-link
|
|
58
|
-
| no-import-assertions
|
|
59
|
-
| no-implicit-public
|
|
60
|
-
| no-infinite-loop
|
|
61
|
-
| no-legacy-peer-deps
|
|
62
|
-
| no-obsolete-attrs
|
|
63
|
-
| no-obsolete-tags
|
|
64
|
-
| no-playwright-empty-fill
|
|
65
|
-
| no-project-as-in-ng-template
|
|
66
|
-
| no-restricted-attr-values
|
|
67
|
-
| no-redundant-type-annotation
|
|
68
|
-
| no-side-effects-in-computed
|
|
69
|
-
| no-signal-reads-after-await-in-reactive-context | Disallow bare signal reads after `await` inside reactive callbacks | ✅ | | |
|
|
70
|
-
| no-string-literal-concat
|
|
71
|
-
| no-untracked-outside-reactive-context
|
|
72
|
-
| no-useless-untracked
|
|
73
|
-
| object-single-line
|
|
74
|
-
| prefer-combined-if-control-flow
|
|
75
|
-
| prefer-deep-imports
|
|
76
|
-
| prefer-multi-arg-push
|
|
77
|
-
| prefer-namespace-keyword
|
|
78
|
-
| prefer-untracked-incidental-signal-reads
|
|
79
|
-
| prefer-untracked-signal-getter
|
|
80
|
-
| quotes
|
|
81
|
-
| require-doctype
|
|
82
|
-
| require-img-alt
|
|
83
|
-
| require-lang
|
|
84
|
-
| require-li-container
|
|
85
|
-
| require-title
|
|
86
|
-
| short-tui-imports
|
|
87
|
-
| single-line-class-property-spacing
|
|
88
|
-
| standalone-imports-sort
|
|
89
|
-
| strict-tui-doc-example
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## array-as-const
|
|
94
|
-
|
|
95
|
-
<sup>`Taiga-specific`</sup> <sup>`Fixable`</sup>
|
|
96
|
-
|
|
97
|
-
Exported arrays containing only class references must be marked with `as const` to preserve the tuple type and enable
|
|
98
|
-
proper type inference.
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
// ❌ error
|
|
102
|
-
export const PROVIDERS = [FooService, BarService];
|
|
103
|
-
|
|
104
|
-
// ✅ after autofix
|
|
105
|
-
export const PROVIDERS = [FooService, BarService] as const;
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## attrs-newline
|
|
111
|
-
|
|
112
|
-
<sup>`Fixable`</sup>
|
|
113
|
-
|
|
114
|
-
Requires line breaks between attributes when a start tag has more than two attributes. This keeps larger HTML tags
|
|
115
|
-
readable and makes attribute diffs much easier to scan in Angular templates.
|
|
116
|
-
|
|
117
|
-
```html
|
|
118
|
-
<!-- ❌ error -->
|
|
119
|
-
<div
|
|
120
|
-
class="b"
|
|
121
|
-
id="a"
|
|
122
|
-
title="c"
|
|
123
|
-
></div>
|
|
124
|
-
|
|
125
|
-
<!-- ✅ after autofix -->
|
|
126
|
-
<div
|
|
127
|
-
class="b"
|
|
128
|
-
id="a"
|
|
129
|
-
title="c"
|
|
130
|
-
></div>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## class-property-naming
|
|
136
|
-
|
|
137
|
-
<sup>`Taiga-specific`</sup> <sup>`Fixable`</sup>
|
|
138
|
-
|
|
139
|
-
Enforce custom naming conventions for class properties based on their TypeScript type. Useful for enforcing project-wide
|
|
140
|
-
patterns (e.g. all `Subject` fields must be called `destroy$`).
|
|
141
|
-
|
|
142
|
-
Requires explicit configuration — not enabled in `recommended` by default.
|
|
143
|
-
|
|
144
|
-
```json
|
|
145
|
-
{
|
|
146
|
-
"@taiga-ui/experience-next/class-property-naming": [
|
|
147
|
-
"error",
|
|
148
|
-
[
|
|
149
|
-
{
|
|
150
|
-
"fieldNames": ["sub", "subscription"],
|
|
151
|
-
"newFieldName": "destroy$",
|
|
152
|
-
"withTypesSpecifier": ["Subject", "Subscription"]
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
]
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
```ts
|
|
160
|
-
// ❌ error
|
|
161
|
-
class MyComponent {
|
|
162
|
-
sub = new Subject<void>();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// ✅ after autofix
|
|
166
|
-
class MyComponent {
|
|
167
|
-
destroy$ = new Subject<void>();
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## decorator-key-sort
|
|
174
|
-
|
|
175
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
176
|
-
|
|
177
|
-
Enforces a consistent key order inside Angular decorator objects (`@Component`, `@Directive`, `@NgModule`, `@Pipe`,
|
|
178
|
-
`@Injectable`). The expected order is passed as configuration.
|
|
179
|
-
|
|
180
|
-
```json
|
|
181
|
-
{
|
|
182
|
-
"@taiga-ui/experience-next/decorator-key-sort": [
|
|
183
|
-
"error",
|
|
184
|
-
{
|
|
185
|
-
"Component": ["standalone", "selector", "imports", "templateUrl", "styleUrl", "changeDetection"],
|
|
186
|
-
"Pipe": ["standalone", "name", "pure"]
|
|
187
|
-
}
|
|
188
|
-
]
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
```ts
|
|
193
|
-
// ❌ error
|
|
194
|
-
@Component({
|
|
195
|
-
templateUrl: './app.component.html',
|
|
196
|
-
selector: 'app-root',
|
|
197
|
-
standalone: true,
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
// ✅ after autofix
|
|
201
|
-
@Component({
|
|
202
|
-
standalone: true,
|
|
203
|
-
selector: 'app-root',
|
|
204
|
-
templateUrl: './app.component.html',
|
|
205
|
-
})
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## element-newline
|
|
211
|
-
|
|
212
|
-
<sup>`Fixable`</sup>
|
|
213
|
-
|
|
214
|
-
Requires line breaks around block-level child nodes. Inline text and inline elements can stay on one line, but block
|
|
215
|
-
content should be visually separated from its container.
|
|
216
|
-
|
|
217
|
-
```html
|
|
218
|
-
<!-- ❌ error -->
|
|
219
|
-
<div><section>Content</section></div>
|
|
220
|
-
|
|
221
|
-
<!-- ✅ after autofix -->
|
|
222
|
-
<div>
|
|
223
|
-
<section>Content</section>
|
|
224
|
-
</div>
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## flat-exports
|
|
230
|
-
|
|
231
|
-
<sup>`Taiga-specific`</sup> <sup>`Fixable`</sup>
|
|
232
|
-
|
|
233
|
-
When an exported `as const` tuple contains another exported `as const` tuple of Angular classes, it should be spread
|
|
234
|
-
rather than nested. This keeps entity collections flat and avoids double-wrapping.
|
|
235
|
-
|
|
236
|
-
```ts
|
|
237
|
-
// ❌ error
|
|
238
|
-
export const TuiTextfield = [TuiTextfieldDirective] as const;
|
|
239
|
-
export const TuiInput = [TuiTextfield, TuiInputDirective] as const;
|
|
240
|
-
|
|
241
|
-
// ✅ after autofix
|
|
242
|
-
export const TuiTextfield = [TuiTextfieldDirective] as const;
|
|
243
|
-
export const TuiInput = [...TuiTextfield, TuiInputDirective] as const;
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## host-attributes-sort
|
|
249
|
-
|
|
250
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
251
|
-
|
|
252
|
-
Sorts Angular `host` metadata entries in `@Component` and `@Directive` using configurable attribute groups, matching the
|
|
253
|
-
same grouping model used for template attributes in Prettier. The recommended config enables the rule with a default
|
|
254
|
-
group order that places `id` before plain attributes, `class`, animation bindings, inputs, two-way bindings, and
|
|
255
|
-
outputs.
|
|
256
|
-
|
|
257
|
-
```ts
|
|
258
|
-
// ❌ error
|
|
259
|
-
@Component({
|
|
260
|
-
host: {
|
|
261
|
-
'(click)': 'handleClick()',
|
|
262
|
-
'[value]': 'value()',
|
|
263
|
-
class: 'cmp',
|
|
264
|
-
id: 'cmp-id',
|
|
265
|
-
},
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
// ✅ after autofix
|
|
269
|
-
@Component({
|
|
270
|
-
host: {
|
|
271
|
-
id: 'cmp-id',
|
|
272
|
-
class: 'cmp',
|
|
273
|
-
'[value]': 'value()',
|
|
274
|
-
'(click)': 'handleClick()',
|
|
275
|
-
},
|
|
276
|
-
})
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
The rule understands the same preset names as `prettier-plugin-organize-attributes`. You can use aggregate presets such
|
|
280
|
-
as `$ANGULAR`, `$HTML`, and `$CODE_GUIDE`, or compose atomic presets such as `$CLASS`, `$ID`, `$ARIA`, `$ANGULAR_INPUT`,
|
|
281
|
-
`$ANGULAR_TWO_WAY_BINDING`, and `$ANGULAR_OUTPUT`.
|
|
282
|
-
|
|
283
|
-
```json
|
|
284
|
-
{
|
|
285
|
-
"@taiga-ui/experience-next/host-attributes-sort": [
|
|
286
|
-
"error",
|
|
287
|
-
{
|
|
288
|
-
"attributeGroups": ["$ANGULAR"]
|
|
289
|
-
}
|
|
290
|
-
]
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
Use `$ANGULAR` when `host` should follow the familiar Angular template-style order:
|
|
295
|
-
`class -> id -> #ref -> *directive -> @animation -> [@animation] -> [(model)] -> [input] -> (output)`.
|
|
296
|
-
|
|
297
|
-
```json
|
|
298
|
-
{
|
|
299
|
-
"@taiga-ui/experience-next/host-attributes-sort": [
|
|
300
|
-
"error",
|
|
301
|
-
{
|
|
302
|
-
"attributeGroups": ["$HTML"]
|
|
303
|
-
}
|
|
304
|
-
]
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
Use `$HTML` when only `class` and `id` should be pulled to the front, and everything else can stay in the trailing
|
|
309
|
-
default group.
|
|
310
|
-
|
|
311
|
-
```json
|
|
312
|
-
{
|
|
313
|
-
"@taiga-ui/experience-next/host-attributes-sort": [
|
|
314
|
-
"error",
|
|
315
|
-
{
|
|
316
|
-
"attributeGroups": ["$CODE_GUIDE"]
|
|
317
|
-
}
|
|
318
|
-
]
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Use `$CODE_GUIDE` for a wider HTML-oriented order: `class`, `id`, `name`, `data-*`, `src`, `for`, `type`, `href`,
|
|
323
|
-
`value`, `title`, `alt`, `role`, `aria-*`.
|
|
324
|
-
|
|
325
|
-
```json
|
|
326
|
-
{
|
|
327
|
-
"@taiga-ui/experience-next/host-attributes-sort": [
|
|
328
|
-
"error",
|
|
329
|
-
{
|
|
330
|
-
"attributeGroups": ["$ID", "$DEFAULT", "$ARIA", "$ANGULAR_OUTPUT"]
|
|
331
|
-
}
|
|
332
|
-
]
|
|
333
|
-
}
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
Use atomic presets when you want a custom order instead of one of the bundled aliases.
|
|
337
|
-
|
|
338
|
-
| Option | Type | Description |
|
|
339
|
-
| --------------------- | --------------------------- | ----------------------------------------------------------------- |
|
|
340
|
-
| `attributeGroups` | `string[]` | Group order. Supports the same preset tokens as Prettier plugins. |
|
|
341
|
-
| `attributeIgnoreCase` | `boolean` | Ignore case when matching custom regexp groups. |
|
|
342
|
-
| `attributeSort` | `'ASC' \| 'DESC' \| 'NONE'` | Sort order inside each matched group. |
|
|
343
|
-
| `decorators` | `string[]` | Decorator names whose `host` metadata should be checked. |
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
## injection-token-description
|
|
348
|
-
|
|
349
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
350
|
-
|
|
351
|
-
The description passed to `new InjectionToken(...)` must contain the name of the variable it is assigned to. The rule
|
|
352
|
-
accepts both direct string descriptions and Angular's `ngDevMode ? '...' : ''` pattern, and the autofix rewrites invalid
|
|
353
|
-
descriptions to the dev-only form. If `ngDevMode` is not declared in the file, the autofix inserts
|
|
354
|
-
`declare const ngDevMode: boolean;` after imports.
|
|
355
|
-
|
|
356
|
-
```ts
|
|
357
|
-
// ❌ error
|
|
358
|
-
import {InjectionToken} from '@angular/core';
|
|
359
|
-
|
|
360
|
-
export const TUI_MY_TOKEN = new InjectionToken<string>('some description');
|
|
361
|
-
|
|
362
|
-
// ✅ after autofix
|
|
363
|
-
import {InjectionToken} from '@angular/core';
|
|
364
|
-
|
|
365
|
-
declare const ngDevMode: boolean;
|
|
366
|
-
|
|
367
|
-
export const TUI_MY_TOKEN = new InjectionToken<string>(ngDevMode ? '[TUI_MY_TOKEN]: some description' : '');
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
## no-commonjs-import-patterns
|
|
373
|
-
|
|
374
|
-
<sup>`✅ Recommended`</sup>
|
|
375
|
-
|
|
376
|
-
Disallows legacy CommonJS interop import patterns that are brittle under modern ESM-oriented toolchains. It reports
|
|
377
|
-
`import foo = require('foo')` and namespace imports that are used as callable values, constructors, or tag functions.
|
|
378
|
-
|
|
379
|
-
```ts
|
|
380
|
-
// ❌ error
|
|
381
|
-
import toolkit = require('@taiga-ui/cdk');
|
|
382
|
-
|
|
383
|
-
import * as createClient from 'legacy-client';
|
|
384
|
-
createClient();
|
|
385
|
-
|
|
386
|
-
// ✅ ok
|
|
387
|
-
import toolkit from '@taiga-ui/cdk';
|
|
388
|
-
|
|
389
|
-
import createClient from 'legacy-client';
|
|
390
|
-
createClient();
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
---
|
|
394
|
-
|
|
395
|
-
## no-deep-imports
|
|
396
|
-
|
|
397
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
398
|
-
|
|
399
|
-
Disallows deep path imports from Taiga UI packages — imports must go through the package root. Works for any
|
|
400
|
-
`@taiga-ui/*` package by default. Autofix strips the deep path.
|
|
401
|
-
|
|
402
|
-
```ts
|
|
403
|
-
// ❌ error
|
|
404
|
-
import {TuiButton} from '@taiga-ui/core/components/button';
|
|
405
|
-
|
|
406
|
-
// ✅ after autofix
|
|
407
|
-
import {TuiButton} from '@taiga-ui/core';
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
```json
|
|
411
|
-
{
|
|
412
|
-
"@taiga-ui/experience-next/no-deep-imports": [
|
|
413
|
-
"error",
|
|
414
|
-
{
|
|
415
|
-
"currentProject": "(?<=projects/)([\\w-]+)",
|
|
416
|
-
"ignoreImports": ["\\?raw", "@taiga-ui/testing/cypress"]
|
|
417
|
-
}
|
|
418
|
-
]
|
|
419
|
-
}
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
| Option | Type | Description |
|
|
423
|
-
| ------------------- | ---------- | -------------------------------------------------------------------------- |
|
|
424
|
-
| `currentProject` | `string` | RegExp to extract the current project name from the file path |
|
|
425
|
-
| `deepImport` | `string` | RegExp to detect the deep import segment (default: `@taiga-ui/` sub-paths) |
|
|
426
|
-
| `importDeclaration` | `string` | RegExp to match import declarations the rule applies to |
|
|
427
|
-
| `ignoreImports` | `string[]` | RegExp patterns for imports to ignore |
|
|
428
|
-
| `projectName` | `string` | RegExp to extract the package name from the import source |
|
|
429
|
-
|
|
430
|
-
---
|
|
431
|
-
|
|
432
|
-
## no-deep-imports-to-indexed-packages
|
|
433
|
-
|
|
434
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
435
|
-
|
|
436
|
-
Disallows deep imports from any external package whose root `index.ts` (or `index.d.ts`) re-exports the same subpath and
|
|
437
|
-
is co-located with a `package.json` or `ng-package.json`. Does not require explicit package lists — resolves via
|
|
438
|
-
TypeScript.
|
|
439
|
-
|
|
440
|
-
```ts
|
|
441
|
-
// ❌ error — @my-lib/index.ts already re-exports this subpath
|
|
442
|
-
import {Foo} from '@my-lib/internal/foo';
|
|
443
|
-
|
|
444
|
-
// ✅
|
|
445
|
-
import {Foo} from '@my-lib/internal';
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
---
|
|
449
|
-
|
|
450
|
-
## no-duplicate-attrs
|
|
451
|
-
|
|
452
|
-
Disallows repeated attributes on the same element. The rule works on Angular template AST and catches duplicate plain
|
|
453
|
-
HTML attributes before they become ambiguous or silently override one another.
|
|
454
|
-
|
|
455
|
-
```html
|
|
456
|
-
<!-- ❌ error -->
|
|
457
|
-
<div
|
|
458
|
-
class="a"
|
|
459
|
-
class="b"
|
|
460
|
-
></div>
|
|
461
|
-
|
|
462
|
-
<!-- ✅ ok -->
|
|
463
|
-
<div
|
|
464
|
-
class="a"
|
|
465
|
-
id="b"
|
|
466
|
-
></div>
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
---
|
|
470
|
-
|
|
471
|
-
## no-duplicate-id
|
|
472
|
-
|
|
473
|
-
Disallows duplicate static `id` values within the same HTML template. This helps keep selectors, label associations, and
|
|
474
|
-
accessibility relationships deterministic.
|
|
475
|
-
|
|
476
|
-
```html
|
|
477
|
-
<!-- ❌ error -->
|
|
478
|
-
<label for="name"></label>
|
|
479
|
-
<input id="name" />
|
|
480
|
-
<div id="name"></div>
|
|
481
|
-
|
|
482
|
-
<!-- ✅ ok -->
|
|
483
|
-
<label for="name"></label>
|
|
484
|
-
<input id="name" />
|
|
485
|
-
<div id="description"></div>
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
---
|
|
489
|
-
|
|
490
|
-
## no-duplicate-in-head
|
|
491
|
-
|
|
492
|
-
Disallows duplicate singleton tags inside `<head>`, including `<title>`, `<base>`, `meta[charset]`,
|
|
493
|
-
`meta[name="viewport"]`, and `link[rel="canonical"]`. Multiple copies of these tags make document metadata unreliable.
|
|
494
|
-
|
|
495
|
-
```html
|
|
496
|
-
<!-- ❌ error -->
|
|
497
|
-
<head>
|
|
498
|
-
<title>One</title>
|
|
499
|
-
<title>Two</title>
|
|
500
|
-
</head>
|
|
501
|
-
|
|
502
|
-
<!-- ✅ ok -->
|
|
503
|
-
<head>
|
|
504
|
-
<title>One</title>
|
|
505
|
-
</head>
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
---
|
|
509
|
-
|
|
510
|
-
## no-fully-untracked-effect
|
|
511
|
-
|
|
512
|
-
<sup>`✅ Recommended`</sup>
|
|
513
|
-
|
|
514
|
-
Reports a reactive callback whose signal reads are all wrapped in `untracked()`. That leaves the callback without
|
|
515
|
-
tracked dependencies, so Angular will not re-run it when those signals change.
|
|
516
|
-
|
|
517
|
-
Applies to `effect()`, `computed()`, `linkedSignal()`, `resource()` callbacks, and `afterRenderEffect()` phases.
|
|
518
|
-
|
|
519
|
-
```ts
|
|
520
|
-
// ❌ error — no tracked reads, effect never re-runs
|
|
521
|
-
effect(() => {
|
|
522
|
-
const value = untracked(() => this.count());
|
|
523
|
-
this.log(value);
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// ✅ ok — count() is read outside untracked, creates a reactive dependency
|
|
527
|
-
effect(() => {
|
|
528
|
-
const value = this.count();
|
|
529
|
-
untracked(() => this.log(value));
|
|
530
|
-
});
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
```ts
|
|
534
|
-
// ❌ error — computed() also loses its dependency
|
|
535
|
-
const doubled = computed(() => untracked(() => this.count() * 2));
|
|
536
|
-
|
|
537
|
-
// ✅ ok
|
|
538
|
-
const doubled = computed(() => this.count() * 2);
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
---
|
|
542
|
-
|
|
543
|
-
## no-href-with-router-link
|
|
544
|
-
|
|
545
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
546
|
-
|
|
547
|
-
> ✅ Included in `recommended` — processed by the angular-eslint template parser (`**/*.html`).
|
|
548
|
-
|
|
549
|
-
Disallows using both `href` and `routerLink` on the same `<a>` element in Angular templates. Autofix removes the `href`
|
|
550
|
-
attribute.
|
|
551
|
-
|
|
552
|
-
```html
|
|
553
|
-
<!-- ❌ error -->
|
|
554
|
-
<a
|
|
555
|
-
href="/home"
|
|
556
|
-
routerLink="/home"
|
|
557
|
-
>
|
|
558
|
-
Home
|
|
559
|
-
</a>
|
|
560
|
-
|
|
561
|
-
<!-- ✅ after autofix -->
|
|
562
|
-
<a routerLink="/home">Home</a>
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
---
|
|
566
|
-
|
|
567
|
-
## no-import-assertions
|
|
568
|
-
|
|
569
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
570
|
-
|
|
571
|
-
Disallows legacy `assert { ... }` import assertions and rewrites them to `with { ... }` import attributes.
|
|
572
|
-
|
|
573
|
-
```ts
|
|
574
|
-
// ❌ error
|
|
575
|
-
import data from './file.json' assert {type: 'json'};
|
|
576
|
-
|
|
577
|
-
// ✅ after autofix
|
|
578
|
-
import data from './file.json' with {type: 'json'};
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
---
|
|
582
|
-
|
|
583
|
-
## no-implicit-public
|
|
584
|
-
|
|
585
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
586
|
-
|
|
587
|
-
Requires an explicit `public` modifier on all class members and constructor parameter properties that are public.
|
|
588
|
-
Constructors are excluded.
|
|
589
|
-
|
|
590
|
-
```ts
|
|
591
|
-
// ❌ error
|
|
592
|
-
class MyService {
|
|
593
|
-
value = 42;
|
|
594
|
-
doSomething(): void {}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// ✅ after autofix
|
|
598
|
-
class MyService {
|
|
599
|
-
public value = 42;
|
|
600
|
-
public doSomething(): void {}
|
|
601
|
-
}
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
---
|
|
605
|
-
|
|
606
|
-
## no-infinite-loop
|
|
607
|
-
|
|
608
|
-
<sup>`✅ Recommended`</sup>
|
|
609
|
-
|
|
610
|
-
Disallows the two loop forms banned by this project: `while (true)` and `for` loops without a condition, including the
|
|
611
|
-
canonical `for (;;)` form. These loops hide the real exit condition inside the body, which makes control flow harder to
|
|
612
|
-
scan and review.
|
|
613
|
-
|
|
614
|
-
```ts
|
|
615
|
-
// ❌ error
|
|
616
|
-
while (true) {
|
|
617
|
-
if (isDone) {
|
|
618
|
-
break;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
process();
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// ✅ ok
|
|
625
|
-
while (!isDone) {
|
|
626
|
-
process();
|
|
627
|
-
}
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
```ts
|
|
631
|
-
// ❌ error
|
|
632
|
-
for (;;) {
|
|
633
|
-
if (queue.length === 0) {
|
|
634
|
-
break;
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
flush(queue.shift());
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// ✅ ok
|
|
641
|
-
for (; queue.length > 0; ) {
|
|
642
|
-
flush(queue.shift());
|
|
643
|
-
}
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
## no-legacy-peer-deps
|
|
649
|
-
|
|
650
|
-
<sup>`✅ Recommended`</sup>
|
|
651
|
-
|
|
652
|
-
> ✅ Included in `recommended` — applied to `**/.npmrc`.
|
|
653
|
-
|
|
654
|
-
Disallows `legacy-peer-deps=true` in `.npmrc`. This npm option bypasses peer dependency resolution and can hide real
|
|
655
|
-
version conflicts in the dependency graph. The preferred fix is to align incompatible package versions instead of
|
|
656
|
-
disabling the resolver.
|
|
657
|
-
|
|
658
|
-
```ini
|
|
659
|
-
# ❌ error
|
|
660
|
-
legacy-peer-deps=true
|
|
661
|
-
|
|
662
|
-
# ✅ ok
|
|
663
|
-
strict-peer-deps=true
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
Comments and empty lines are ignored, so the rule only reports an active `legacy-peer-deps=true` entry.
|
|
667
|
-
|
|
668
|
-
---
|
|
669
|
-
|
|
670
|
-
## no-obsolete-attrs
|
|
671
|
-
|
|
672
|
-
Disallows obsolete HTML attributes such as presentational or deprecated legacy attributes that should be replaced with
|
|
673
|
-
modern HTML or CSS.
|
|
674
|
-
|
|
675
|
-
```html
|
|
676
|
-
<!-- ❌ error -->
|
|
677
|
-
<table border="1"></table>
|
|
678
|
-
|
|
679
|
-
<!-- ✅ ok -->
|
|
680
|
-
<table class="with-border"></table>
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
---
|
|
684
|
-
|
|
685
|
-
## no-obsolete-tags
|
|
686
|
-
|
|
687
|
-
Disallows obsolete HTML tags that should no longer appear in modern markup. This keeps templates aligned with current
|
|
688
|
-
HTML standards and avoids legacy presentational elements.
|
|
689
|
-
|
|
690
|
-
```html
|
|
691
|
-
<!-- ❌ error -->
|
|
692
|
-
<center>Title</center>
|
|
693
|
-
|
|
694
|
-
<!-- ✅ ok -->
|
|
695
|
-
<div class="centered">Title</div>
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
---
|
|
699
|
-
|
|
700
|
-
## no-playwright-empty-fill
|
|
701
|
-
|
|
702
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
703
|
-
|
|
704
|
-
In Playwright tests, calling `.fill('')` on a locator should be replaced with `.clear()` — it is the idiomatic way to
|
|
705
|
-
empty a field and communicates intent more clearly.
|
|
706
|
-
|
|
707
|
-
```ts
|
|
708
|
-
// ❌ error
|
|
709
|
-
await page.getByLabel('Name').fill('');
|
|
710
|
-
|
|
711
|
-
// ✅ after autofix
|
|
712
|
-
await page.getByLabel('Name').clear();
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
---
|
|
716
|
-
|
|
717
|
-
## no-project-as-in-ng-template
|
|
718
|
-
|
|
719
|
-
<sup>`✅ Recommended`</sup>
|
|
720
|
-
|
|
721
|
-
`ngProjectAs` has no effect when the element is inside an `<ng-template>`, `*ngTemplateOutlet`, `*ngComponentOutlet`, or
|
|
722
|
-
`*polymorpheusOutlet`. Content instantiated through these dynamic outlets does not participate in Angular's static
|
|
723
|
-
content projection, so the attribute is silently ignored at runtime.
|
|
724
|
-
|
|
725
|
-
```html
|
|
726
|
-
<!-- ❌ error — inside <ng-template> -->
|
|
727
|
-
<ng-template #tpl>
|
|
728
|
-
<div ngProjectAs="[someSlot]">content</div>
|
|
729
|
-
</ng-template>
|
|
730
|
-
|
|
731
|
-
<!-- ❌ error — on the outlet host itself -->
|
|
732
|
-
<ng-container
|
|
733
|
-
*ngTemplateOutlet="tpl"
|
|
734
|
-
ngProjectAs="[someSlot]"
|
|
735
|
-
></ng-container>
|
|
736
|
-
|
|
737
|
-
<!-- ❌ error — polymorpheusOutlet -->
|
|
738
|
-
<ng-container
|
|
739
|
-
*polymorpheusOutlet="content"
|
|
740
|
-
ngProjectAs="someSlot"
|
|
741
|
-
></ng-container>
|
|
742
|
-
|
|
743
|
-
<!-- ✅ ok — static content projection -->
|
|
744
|
-
<div ngProjectAs="[someSlot]">content</div>
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
---
|
|
748
|
-
|
|
749
|
-
## no-restricted-attr-values
|
|
750
|
-
|
|
751
|
-
<sup>`Taiga-specific`</sup>
|
|
752
|
-
|
|
753
|
-
Disallows configured string values for Angular template attributes, including both plain HTML attributes and literal
|
|
754
|
-
string bindings like `[icon]="'@tui.x'"`. This is useful when a project wants to ban hardcoded design-system tokens in
|
|
755
|
-
markup and require them to come from component inputs or options objects instead.
|
|
756
|
-
|
|
757
|
-
```json
|
|
758
|
-
{
|
|
759
|
-
"@taiga-ui/experience-next/no-restricted-attr-values": [
|
|
760
|
-
"error",
|
|
761
|
-
{
|
|
762
|
-
"attrPatterns": ["iconStart", "iconEnd", "icon"],
|
|
763
|
-
"attrValuePatterns": ["@tui"],
|
|
764
|
-
"message": "Icons must be configured"
|
|
765
|
-
}
|
|
766
|
-
]
|
|
767
|
-
}
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
```html
|
|
771
|
-
<!-- ❌ error -->
|
|
772
|
-
<button iconStart="@tui.x"></button>
|
|
773
|
-
<tui-icon [icon]="'@tui.chevron-down'"></tui-icon>
|
|
774
|
-
|
|
775
|
-
<!-- ✅ ok -->
|
|
776
|
-
<button [iconStart]="options.iconStart"></button>
|
|
777
|
-
<tui-icon [icon]="options.icon"></tui-icon>
|
|
778
|
-
```
|
|
779
|
-
|
|
780
|
-
---
|
|
781
|
-
|
|
782
|
-
## no-string-literal-concat
|
|
783
|
-
|
|
784
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
785
|
-
|
|
786
|
-
Disallows concatenating string literals with `+`. Adjacent string literals are always mergeable into one — splitting
|
|
787
|
-
them with `+` adds noise without benefit, and multi-line splits are especially easy to miss.
|
|
788
|
-
|
|
789
|
-
Replaces the built-in `no-useless-concat` rule, which only catches same-line concatenation.
|
|
790
|
-
|
|
791
|
-
```ts
|
|
792
|
-
// ❌ error
|
|
793
|
-
const msg = 'Hello, ' + 'world!';
|
|
794
|
-
|
|
795
|
-
// ✅ after autofix
|
|
796
|
-
const msg = 'Hello, world!';
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
```ts
|
|
800
|
-
// ❌ error — also caught across lines
|
|
801
|
-
it(
|
|
802
|
-
'returns the last day of month when' +
|
|
803
|
-
' the result month has fewer days',
|
|
804
|
-
() => { ... },
|
|
805
|
-
);
|
|
806
|
-
|
|
807
|
-
// ✅ after autofix
|
|
808
|
-
it('returns the last day of month when the result month has fewer days', () => {
|
|
809
|
-
...
|
|
810
|
-
});
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
```ts
|
|
814
|
-
// ❌ error — string variables concatenated with +
|
|
815
|
-
const a = 'hello';
|
|
816
|
-
const b = 'world';
|
|
817
|
-
const c = a + b;
|
|
818
|
-
|
|
819
|
-
// ✅ after autofix
|
|
820
|
-
const c = `${a}${b}`;
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
When the concatenation is a **direct expression inside a template literal**, the parts are inlined into the outer
|
|
824
|
-
template instead of producing a nested template literal:
|
|
825
|
-
|
|
826
|
-
```ts
|
|
827
|
-
// ❌ error
|
|
828
|
-
const url = `${base}${path + query}`;
|
|
829
|
-
|
|
830
|
-
// ✅ after autofix — inlined, no nesting
|
|
831
|
-
const url = `${base}${path}${query}`;
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
```ts
|
|
835
|
-
// ❌ error — literal concat inside template
|
|
836
|
-
const mask = `${'HH' + ':MM'}`;
|
|
837
|
-
|
|
838
|
-
// ✅ after autofix
|
|
839
|
-
const mask = `HH:MM`;
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
When the concatenation appears **inside a method call or other expression** within a template literal, the rule skips it
|
|
843
|
-
to avoid creating unreadable nested template literals like `` `${`${a}${b}`.method()}` ``.
|
|
844
|
-
|
|
845
|
-
The rule also **flattens already-nested template literals** produced by earlier autofixes or written by hand:
|
|
846
|
-
|
|
847
|
-
```ts
|
|
848
|
-
// ❌ error
|
|
849
|
-
const s = `${`${dateMode}${dateTimeSeparator}`}HH:MM`;
|
|
850
|
-
|
|
851
|
-
// ✅ after autofix
|
|
852
|
-
const s = `${dateMode}${dateTimeSeparator}HH:MM`;
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
Concatenation that uses **inline comments between parts** is intentionally left untouched, as the comments serve as
|
|
856
|
-
documentation:
|
|
857
|
-
|
|
858
|
-
```ts
|
|
859
|
-
// ✅ not flagged — comments are preserved
|
|
860
|
-
const urlRegex =
|
|
861
|
-
String.raw`^([a-zA-Z]+:\/\/)?` + // protocol
|
|
862
|
-
String.raw`([\w-]+\.)+[\w]{2,}` + // domain
|
|
863
|
-
String.raw`(\/\S*)?$`; // path
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
> For mixed concatenation (`'prefix' + variable`) use the standard `prefer-template` rule, which is already enabled in
|
|
867
|
-
> `recommended`. Template literals (`` `foo` + `bar` ``) and tagged templates are not flagged by this rule.
|
|
868
|
-
|
|
869
|
-
---
|
|
870
|
-
|
|
871
|
-
## no-useless-untracked
|
|
872
|
-
|
|
873
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
874
|
-
|
|
875
|
-
Inside a reactive callback, `untracked()` is only meaningful when its inner function reads signals or intentionally
|
|
876
|
-
wraps opaque external code that may read signals. It can also be a valid escape hatch when a reactive callback needs to
|
|
877
|
-
create another reactive owner such as `effect()` without inheriting the ambient reactive context. Wrapping code with no
|
|
878
|
-
signal reads and no such escape-hatch purpose in `untracked()` is noise. Autofix unwraps the callback in-place when that
|
|
879
|
-
is structurally safe and removes the `untracked` import when it is no longer used elsewhere. Snapshot reads that later
|
|
880
|
-
influence branching are still valid and are not reported, because Angular allows incidental reads inside `effect()` and
|
|
881
|
-
similar reactive callbacks.
|
|
882
|
-
|
|
883
|
-
```ts
|
|
884
|
-
// ❌ error — no signal reads inside untracked, wrapper is pointless
|
|
885
|
-
effect(() => {
|
|
886
|
-
untracked(() => {
|
|
887
|
-
this.count.set(0);
|
|
888
|
-
});
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
// ✅ after autofix
|
|
892
|
-
effect(() => {
|
|
893
|
-
this.count.set(0);
|
|
894
|
-
});
|
|
895
|
-
```
|
|
896
|
-
|
|
897
|
-
```ts
|
|
898
|
-
// ✅ ok — snapshot reads may influence control flow without becoming dependencies
|
|
899
|
-
effect(() => {
|
|
900
|
-
const value = untracked(() => this.value());
|
|
901
|
-
|
|
902
|
-
if (this.showAdjacent() && value !== null) {
|
|
903
|
-
this.month.set(value);
|
|
904
|
-
}
|
|
905
|
-
});
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
```ts
|
|
909
|
-
// ✅ ok — linkedSignal fallback may intentionally read a snapshot
|
|
910
|
-
const activeYear = linkedSignal(() => {
|
|
911
|
-
const year = this.year();
|
|
912
|
-
|
|
913
|
-
if (year) {
|
|
914
|
-
return year;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
const value = untracked(() => this.value());
|
|
918
|
-
|
|
919
|
-
return value ?? TODAY;
|
|
920
|
-
});
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
```ts
|
|
924
|
-
// ✅ ok — wrapping external code is a valid Angular use-case
|
|
925
|
-
effect(() => {
|
|
926
|
-
const user = this.user();
|
|
927
|
-
untracked(() => this.logger.log(user));
|
|
928
|
-
});
|
|
929
|
-
```
|
|
930
|
-
|
|
931
|
-
```ts
|
|
932
|
-
// ✅ ok — creating a nested effect() may need to escape the current reactive context
|
|
933
|
-
const doubled = computed(() => {
|
|
934
|
-
untracked(() => {
|
|
935
|
-
effect(() => {
|
|
936
|
-
console.log(this.count());
|
|
937
|
-
});
|
|
938
|
-
});
|
|
939
|
-
|
|
940
|
-
return this.count() * 2;
|
|
941
|
-
});
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
---
|
|
945
|
-
|
|
946
|
-
## no-side-effects-in-computed
|
|
947
|
-
|
|
948
|
-
<sup>`✅ Recommended`</sup>
|
|
949
|
-
|
|
950
|
-
`computed()` should only derive a value from its inputs. This rule reports observable side effects inside Angular
|
|
951
|
-
`computed()` callbacks, including signal writes (`.set()`, `.update()`, `.mutate()`), `effect()`, `inject()`,
|
|
952
|
-
assignments to captured state, `++/--`, `delete`, property mutations on objects that were not created inside the
|
|
953
|
-
computation itself, and calls to local helper functions or methods when their bodies perform those operations.
|
|
954
|
-
|
|
955
|
-
```ts
|
|
956
|
-
// ❌ error
|
|
957
|
-
import {computed, signal} from '@angular/core';
|
|
958
|
-
|
|
959
|
-
const source = signal(0);
|
|
960
|
-
const target = signal(0);
|
|
961
|
-
|
|
962
|
-
function syncTarget(): void {
|
|
963
|
-
target.set(source() + 1);
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
const derived = computed(() => {
|
|
967
|
-
syncTarget();
|
|
968
|
-
return target();
|
|
969
|
-
});
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
```ts
|
|
973
|
-
// ✅ ok
|
|
974
|
-
import {computed, signal} from '@angular/core';
|
|
975
|
-
|
|
976
|
-
const source = signal(0);
|
|
977
|
-
const derived = computed(() => source() + 1);
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
---
|
|
981
|
-
|
|
982
|
-
## no-signal-reads-after-await-in-reactive-context
|
|
983
|
-
|
|
984
|
-
<sup>`✅ Recommended`</sup>
|
|
985
|
-
|
|
986
|
-
Angular tracks signal reads only in synchronous code. If a reactive callback crosses an async boundary, any bare signal
|
|
987
|
-
read after `await` will not become a dependency. Snapshot before `await` when you need the earlier value, or make an
|
|
988
|
-
intentional post-`await` current-value read explicit with `untracked(...)`.
|
|
989
|
-
|
|
990
|
-
```ts
|
|
991
|
-
// ❌ error
|
|
992
|
-
effect(async () => {
|
|
993
|
-
await this.fetchUser();
|
|
994
|
-
console.log(this.theme());
|
|
995
|
-
});
|
|
996
|
-
|
|
997
|
-
// ✅ ok
|
|
998
|
-
effect(async () => {
|
|
999
|
-
const theme = this.theme();
|
|
1000
|
-
await this.fetchUser();
|
|
1001
|
-
console.log(theme);
|
|
1002
|
-
});
|
|
1003
|
-
```
|
|
1004
|
-
|
|
1005
|
-
```ts
|
|
1006
|
-
// ✅ ok — explicit current-value read after await
|
|
1007
|
-
effect(async () => {
|
|
1008
|
-
await this.fetchUser();
|
|
1009
|
-
console.log(untracked(this.theme));
|
|
1010
|
-
});
|
|
1011
|
-
```
|
|
1012
|
-
|
|
1013
|
-
---
|
|
1014
|
-
|
|
1015
|
-
## no-untracked-outside-reactive-context
|
|
1016
|
-
|
|
1017
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1018
|
-
|
|
1019
|
-
`untracked()` usually only affects signal reads that happen inside the synchronous body of a reactive callback. In
|
|
1020
|
-
ordinary non-reactive code or nested callbacks it usually does not prevent dependency tracking and only adds noise. This
|
|
1021
|
-
rule reports those cases, but intentionally allows a few explicit escape hatches: post-`await` reads inside a reactive
|
|
1022
|
-
callback when `untracked()` is used to document an intentional current-value snapshot, imperative Angular hooks such as
|
|
1023
|
-
`@Pipe().transform`, `ControlValueAccessor.writeValue`, `registerOnChange` including patched accessors such as
|
|
1024
|
-
`accessor.writeValue = (...) => {}`, callback-form wrappers used inside deferred scheduler / event-handler callbacks,
|
|
1025
|
-
and narrow lazy DI factory wrappers like `InjectionToken({factory})` / `useFactory` when they guard creation of a
|
|
1026
|
-
reactive owner such as `effect()` against an accidental ambient reactive context. For the narrow case
|
|
1027
|
-
`untracked(() => effect(...))` and similar outer wrappers around a reactive call in ordinary code, autofix removes only
|
|
1028
|
-
the useless outer `untracked()` wrapper.
|
|
1029
|
-
|
|
1030
|
-
```ts
|
|
1031
|
-
// ❌ error
|
|
1032
|
-
const snapshot = untracked(this.user);
|
|
1033
|
-
|
|
1034
|
-
effect(() => {
|
|
1035
|
-
button.addEventListener('click', () => {
|
|
1036
|
-
console.log(untracked(this.user));
|
|
1037
|
-
});
|
|
1038
|
-
});
|
|
1039
|
-
```
|
|
1040
|
-
|
|
1041
|
-
```ts
|
|
1042
|
-
// ✅ ok
|
|
1043
|
-
effect(() => {
|
|
1044
|
-
console.log(untracked(this.user));
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
const snapshot = computed(() => untracked(this.user));
|
|
1048
|
-
```
|
|
1049
|
-
|
|
1050
|
-
```ts
|
|
1051
|
-
// ✅ ok — after await, untracked can mark an intentional current snapshot
|
|
1052
|
-
effect(async () => {
|
|
1053
|
-
await this.refresh();
|
|
1054
|
-
|
|
1055
|
-
if (untracked(this.user) !== previousUser) {
|
|
1056
|
-
console.log('changed');
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
```
|
|
1060
|
-
|
|
1061
|
-
```ts
|
|
1062
|
-
// ❌ error
|
|
1063
|
-
untracked(() => {
|
|
1064
|
-
effect(() => {
|
|
1065
|
-
console.log(this.user());
|
|
1066
|
-
});
|
|
1067
|
-
});
|
|
1068
|
-
|
|
1069
|
-
// ✅ after autofix
|
|
1070
|
-
effect(() => {
|
|
1071
|
-
console.log(this.user());
|
|
1072
|
-
});
|
|
1073
|
-
```
|
|
1074
|
-
|
|
1075
|
-
```ts
|
|
1076
|
-
// ✅ ok — imperative Angular hooks may still need untracked
|
|
1077
|
-
@Pipe({name: 'demo', pure: false})
|
|
1078
|
-
export class DemoPipe implements PipeTransform {
|
|
1079
|
-
private readonly value = signal('');
|
|
1080
|
-
|
|
1081
|
-
transform(next: string): string {
|
|
1082
|
-
untracked(() => this.value.set(next));
|
|
1083
|
-
return this.value();
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
```
|
|
1087
|
-
|
|
1088
|
-
```ts
|
|
1089
|
-
// ✅ ok — deferred callback wrappers may execute under reactive control later
|
|
1090
|
-
const update = (): void => untracked(() => value.set(input.value));
|
|
1091
|
-
|
|
1092
|
-
input.addEventListener('input', update, {capture: true});
|
|
1093
|
-
```
|
|
1094
|
-
|
|
1095
|
-
```ts
|
|
1096
|
-
// ✅ ok — lazy DI factories may first execute from an ambient reactive context
|
|
1097
|
-
export const TOKEN = new InjectionToken<void>('TOKEN', {
|
|
1098
|
-
factory: () => {
|
|
1099
|
-
untracked(() => {
|
|
1100
|
-
effect(() => {
|
|
1101
|
-
console.log(count());
|
|
1102
|
-
});
|
|
1103
|
-
});
|
|
1104
|
-
},
|
|
1105
|
-
});
|
|
1106
|
-
```
|
|
1107
|
-
|
|
1108
|
-
---
|
|
1109
|
-
|
|
1110
|
-
## object-single-line
|
|
1111
|
-
|
|
1112
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1113
|
-
|
|
1114
|
-
Single-property object literals that fit within `printWidth` characters on one line are collapsed to a single line.
|
|
1115
|
-
Compatible with Prettier formatting.
|
|
1116
|
-
|
|
1117
|
-
```ts
|
|
1118
|
-
// ❌ error
|
|
1119
|
-
const x = {
|
|
1120
|
-
foo: bar,
|
|
1121
|
-
};
|
|
1122
|
-
|
|
1123
|
-
// ✅ after autofix
|
|
1124
|
-
const x = {foo: bar};
|
|
1125
|
-
```
|
|
1126
|
-
|
|
1127
|
-
```json
|
|
1128
|
-
{
|
|
1129
|
-
"@taiga-ui/experience-next/object-single-line": ["error", {"printWidth": 90}]
|
|
1130
|
-
}
|
|
1131
|
-
```
|
|
1132
|
-
|
|
1133
|
-
| Option | Type | Default | Description |
|
|
1134
|
-
| ------------ | -------- | ------- | ------------------------------------- |
|
|
1135
|
-
| `printWidth` | `number` | `90` | Maximum line length to allow inlining |
|
|
1136
|
-
|
|
1137
|
-
---
|
|
1138
|
-
|
|
1139
|
-
## prefer-combined-if-control-flow
|
|
1140
|
-
|
|
1141
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1142
|
-
|
|
1143
|
-
Combine consecutive `if` statements when they have no `else` branch and use the same `return`, `break`, `continue`, or
|
|
1144
|
-
`throw` statement. The autofix merges their conditions with `||`, while intentionally skipping cases with intervening
|
|
1145
|
-
code or comments that should remain a separate control-flow boundary.
|
|
1146
|
-
|
|
1147
|
-
```ts
|
|
1148
|
-
// ❌ error
|
|
1149
|
-
while (true) {
|
|
1150
|
-
if (a) continue;
|
|
1151
|
-
if (b && c) continue;
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
// ✅ after autofix
|
|
1155
|
-
while (true) {
|
|
1156
|
-
if (a || (b && c)) continue;
|
|
1157
|
-
}
|
|
1158
|
-
```
|
|
1159
|
-
|
|
1160
|
-
```ts
|
|
1161
|
-
// ❌ error
|
|
1162
|
-
if (a || b) {
|
|
1163
|
-
return;
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
if (c) {
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
// ✅ after autofix
|
|
1171
|
-
if (a || b || c) {
|
|
1172
|
-
return;
|
|
1173
|
-
}
|
|
1174
|
-
```
|
|
1175
|
-
|
|
1176
|
-
```ts
|
|
1177
|
-
// ❌ error
|
|
1178
|
-
if (isInvalid) return result;
|
|
1179
|
-
|
|
1180
|
-
if (isLegacy && shouldStop) return result;
|
|
1181
|
-
|
|
1182
|
-
// ✅ after autofix
|
|
1183
|
-
if (isInvalid || (isLegacy && shouldStop)) return result;
|
|
1184
|
-
```
|
|
1185
|
-
|
|
1186
|
-
```ts
|
|
1187
|
-
// ❌ error
|
|
1188
|
-
while (true) {
|
|
1189
|
-
if (isDone) break;
|
|
1190
|
-
if (hasError) break;
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
// ✅ after autofix
|
|
1194
|
-
while (true) {
|
|
1195
|
-
if (isDone || hasError) break;
|
|
1196
|
-
}
|
|
1197
|
-
```
|
|
1198
|
-
|
|
1199
|
-
```ts
|
|
1200
|
-
// ❌ error
|
|
1201
|
-
if (isFatal) throw error;
|
|
1202
|
-
|
|
1203
|
-
if (isExpired && shouldAbort) throw error;
|
|
1204
|
-
|
|
1205
|
-
// ✅ after autofix
|
|
1206
|
-
if (isFatal || (isExpired && shouldAbort)) throw error;
|
|
1207
|
-
```
|
|
1208
|
-
|
|
1209
|
-
```ts
|
|
1210
|
-
// not changed — different control flow
|
|
1211
|
-
while (true) {
|
|
1212
|
-
if (isDone) continue;
|
|
1213
|
-
if (hasError) break;
|
|
1214
|
-
}
|
|
1215
|
-
```
|
|
1216
|
-
|
|
1217
|
-
```ts
|
|
1218
|
-
// not changed — comment keeps branches separate
|
|
1219
|
-
if (a) {
|
|
1220
|
-
return value;
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
// explain why this branch exists
|
|
1224
|
-
if (b) {
|
|
1225
|
-
return value;
|
|
1226
|
-
}
|
|
1227
|
-
```
|
|
1228
|
-
|
|
1229
|
-
---
|
|
1230
|
-
|
|
1231
|
-
## prefer-deep-imports
|
|
1232
|
-
|
|
1233
|
-
<sup>`Taiga-specific`</sup> <sup>`Fixable`</sup>
|
|
1234
|
-
|
|
1235
|
-
Enforce imports from the deepest available entry point of Taiga UI packages.
|
|
1236
|
-
|
|
1237
|
-
```json
|
|
1238
|
-
{
|
|
1239
|
-
"@taiga-ui/experience-next/prefer-deep-imports": [
|
|
1240
|
-
"error",
|
|
1241
|
-
{
|
|
1242
|
-
"importFilter": ["@taiga-ui/core", "@taiga-ui/kit"],
|
|
1243
|
-
"strict": true
|
|
1244
|
-
}
|
|
1245
|
-
]
|
|
1246
|
-
}
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
Use `strict` to forbid imports from intermediate entry points when deeper ones exist (recommended for CI).
|
|
1250
|
-
|
|
1251
|
-
---
|
|
1252
|
-
|
|
1253
|
-
## prefer-multi-arg-push
|
|
1254
|
-
|
|
1255
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1256
|
-
|
|
1257
|
-
Combine consecutive `.push()` calls on the same array into a single multi-argument call.
|
|
1258
|
-
|
|
1259
|
-
```ts
|
|
1260
|
-
// ❌ error
|
|
1261
|
-
output.push('# Getting Started');
|
|
1262
|
-
output.push('');
|
|
1263
|
-
|
|
1264
|
-
// ✅ after autofix
|
|
1265
|
-
output.push('# Getting Started', '');
|
|
1266
|
-
```
|
|
1267
|
-
|
|
1268
|
-
---
|
|
1269
|
-
|
|
1270
|
-
## prefer-namespace-keyword
|
|
1271
|
-
|
|
1272
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1273
|
-
|
|
1274
|
-
Prefers `namespace Foo {}` over the older `module Foo {}` syntax for TypeScript namespace declarations. External module
|
|
1275
|
-
augmentations such as `declare module 'pkg' {}` are ignored.
|
|
1276
|
-
|
|
1277
|
-
```ts
|
|
1278
|
-
// ❌ error
|
|
1279
|
-
module Foo.Bar {
|
|
1280
|
-
export type Value = string;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
// ✅ after autofix
|
|
1284
|
-
namespace Foo.Bar {
|
|
1285
|
-
export type Value = string;
|
|
1286
|
-
}
|
|
1287
|
-
```
|
|
1288
|
-
|
|
1289
|
-
---
|
|
1290
|
-
|
|
1291
|
-
## prefer-untracked-incidental-signal-reads
|
|
1292
|
-
|
|
1293
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1294
|
-
|
|
1295
|
-
Inside a reactive callback, flags direct signal reads that look like snapshot-only values passed into writable-signal
|
|
1296
|
-
writes such as `.set()` or into DOM side-effect calls such as `requestFullscreen(...)`. These reads are likely
|
|
1297
|
-
incidental and should usually not create their own dependency. The rule only reports when the callback already has
|
|
1298
|
-
another tracked dependency outside the flagged consumer call, and autofix wraps the incidental read with `untracked()`
|
|
1299
|
-
while adding the import if needed. If the read is intentionally reactive, disable the rule for that line.
|
|
1300
|
-
|
|
1301
|
-
```ts
|
|
1302
|
-
// ❌ error
|
|
1303
|
-
effect(() => {
|
|
1304
|
-
if (this.options().length) {
|
|
1305
|
-
this.input.value.set(this.stringified());
|
|
1306
|
-
}
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
// ✅ after autofix
|
|
1310
|
-
effect(() => {
|
|
1311
|
-
if (this.options().length) {
|
|
1312
|
-
this.input.value.set(untracked(() => this.stringified()));
|
|
1313
|
-
}
|
|
1314
|
-
});
|
|
1315
|
-
```
|
|
1316
|
-
|
|
1317
|
-
```ts
|
|
1318
|
-
// ❌ error
|
|
1319
|
-
effect(() => {
|
|
1320
|
-
if (this.options().length) {
|
|
1321
|
-
const value = this.stringified();
|
|
1322
|
-
|
|
1323
|
-
this.input.value.set(value);
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
// ✅ after autofix
|
|
1328
|
-
effect(() => {
|
|
1329
|
-
if (this.options().length) {
|
|
1330
|
-
const value = untracked(() => this.stringified());
|
|
1331
|
-
|
|
1332
|
-
this.input.value.set(value);
|
|
1333
|
-
}
|
|
1334
|
-
});
|
|
1335
|
-
```
|
|
1336
|
-
|
|
1337
|
-
```ts
|
|
1338
|
-
// ❌ error
|
|
1339
|
-
effect(async () => {
|
|
1340
|
-
if (this.tuiFullscreen()) {
|
|
1341
|
-
await this.root()?.requestFullscreen(this.options());
|
|
1342
|
-
}
|
|
1343
|
-
});
|
|
1344
|
-
|
|
1345
|
-
// ✅ after autofix
|
|
1346
|
-
effect(async () => {
|
|
1347
|
-
if (this.tuiFullscreen()) {
|
|
1348
|
-
await this.root()?.requestFullscreen(untracked(() => this.options()));
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
```
|
|
1352
|
-
|
|
1353
|
-
---
|
|
1354
|
-
|
|
1355
|
-
## prefer-untracked-signal-getter
|
|
1356
|
-
|
|
1357
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1358
|
-
|
|
1359
|
-
When `untracked()` wraps only a single signal getter, prefer passing that getter directly. This keeps the code shorter
|
|
1360
|
-
while preserving the same untracked signal read semantics. The rule intentionally skips real TypeScript getters, because
|
|
1361
|
-
property access would happen before `untracked()` starts.
|
|
1362
|
-
|
|
1363
|
-
```ts
|
|
1364
|
-
// ❌ error
|
|
1365
|
-
const snapshot = untracked(() => this.counter());
|
|
1366
|
-
|
|
1367
|
-
// ✅ after autofix
|
|
1368
|
-
const snapshot = untracked(this.counter);
|
|
1369
|
-
```
|
|
1370
|
-
|
|
1371
|
-
---
|
|
1372
|
-
|
|
1373
|
-
## quotes
|
|
1374
|
-
|
|
1375
|
-
<sup>`Fixable`</sup>
|
|
1376
|
-
|
|
1377
|
-
Enforces double quotes around HTML attribute values, including Angular template bindings written in markup. It also adds
|
|
1378
|
-
missing quotes when the attribute value is currently unquoted.
|
|
1379
|
-
|
|
1380
|
-
```html
|
|
1381
|
-
<!-- ❌ error -->
|
|
1382
|
-
<div
|
|
1383
|
-
class="foo"
|
|
1384
|
-
title="bar"
|
|
1385
|
-
></div>
|
|
1386
|
-
|
|
1387
|
-
<!-- ✅ after autofix -->
|
|
1388
|
-
<div
|
|
1389
|
-
class="foo"
|
|
1390
|
-
title="bar"
|
|
1391
|
-
></div>
|
|
1392
|
-
```
|
|
1393
|
-
|
|
1394
|
-
---
|
|
1395
|
-
|
|
1396
|
-
## require-doctype
|
|
1397
|
-
|
|
1398
|
-
<sup>`Fixable`</sup>
|
|
1399
|
-
|
|
1400
|
-
Requires `<!DOCTYPE html>` at the beginning of HTML documents. Even though Angular's template parser does not expose
|
|
1401
|
-
doctype nodes directly, the rule still validates and autofixes the source text.
|
|
1402
|
-
|
|
1403
|
-
```html
|
|
1404
|
-
<!-- ❌ error -->
|
|
1405
|
-
<html lang="en"></html>
|
|
1406
|
-
|
|
1407
|
-
<!-- ✅ after autofix -->
|
|
1408
|
-
<!DOCTYPE html>
|
|
1409
|
-
<html lang="en"></html>
|
|
1410
|
-
```
|
|
1411
|
-
|
|
1412
|
-
---
|
|
1413
|
-
|
|
1414
|
-
## require-img-alt
|
|
1415
|
-
|
|
1416
|
-
Requires an accessible text alternative on `<img>` elements. The rule accepts plain `alt`, `[alt]`, and `[attr.alt]` so
|
|
1417
|
-
it works naturally with Angular bindings.
|
|
1418
|
-
|
|
1419
|
-
```html
|
|
1420
|
-
<!-- ❌ error -->
|
|
1421
|
-
<img src="avatar.png" />
|
|
1422
|
-
|
|
1423
|
-
<!-- ✅ ok -->
|
|
1424
|
-
<img
|
|
1425
|
-
src="avatar.png"
|
|
1426
|
-
alt="Profile"
|
|
1427
|
-
/>
|
|
1428
|
-
<img
|
|
1429
|
-
[src]="avatar"
|
|
1430
|
-
[attr.alt]="description"
|
|
1431
|
-
/>
|
|
1432
|
-
```
|
|
1433
|
-
|
|
1434
|
-
---
|
|
1435
|
-
|
|
1436
|
-
## require-lang
|
|
1437
|
-
|
|
1438
|
-
Requires a non-empty `lang` attribute on the root `<html>` element. Bound Angular forms such as `[attr.lang]` are also
|
|
1439
|
-
accepted.
|
|
1440
|
-
|
|
1441
|
-
```html
|
|
1442
|
-
<!-- ❌ error -->
|
|
1443
|
-
<html>
|
|
1444
|
-
<body></body>
|
|
1445
|
-
</html>
|
|
1446
|
-
|
|
1447
|
-
<!-- ✅ ok -->
|
|
1448
|
-
<html lang="en">
|
|
1449
|
-
<body></body>
|
|
1450
|
-
</html>
|
|
1451
|
-
<html [attr.lang]="locale()">
|
|
1452
|
-
<body></body>
|
|
1453
|
-
</html>
|
|
1454
|
-
```
|
|
1455
|
-
|
|
1456
|
-
---
|
|
1457
|
-
|
|
1458
|
-
## require-li-container
|
|
1459
|
-
|
|
1460
|
-
Requires `<li>` elements to appear inside `<ul>`, `<ol>`, or `<menu>`. This prevents structurally invalid list markup in
|
|
1461
|
-
Angular templates.
|
|
1462
|
-
|
|
1463
|
-
```html
|
|
1464
|
-
<!-- ❌ error -->
|
|
1465
|
-
<div><li>Item</li></div>
|
|
1466
|
-
|
|
1467
|
-
<!-- ✅ ok -->
|
|
1468
|
-
<ul>
|
|
1469
|
-
<li>Item</li>
|
|
1470
|
-
</ul>
|
|
1471
|
-
```
|
|
1472
|
-
|
|
1473
|
-
---
|
|
1474
|
-
|
|
1475
|
-
## require-title
|
|
1476
|
-
|
|
1477
|
-
Requires a non-empty `<title>` inside `<head>`. Plain text and Angular interpolation are both treated as valid title
|
|
1478
|
-
content.
|
|
1479
|
-
|
|
1480
|
-
```html
|
|
1481
|
-
<!-- ❌ error -->
|
|
1482
|
-
<html lang="en">
|
|
1483
|
-
<head></head>
|
|
1484
|
-
<body></body>
|
|
1485
|
-
</html>
|
|
1486
|
-
|
|
1487
|
-
<!-- ✅ ok -->
|
|
1488
|
-
<html lang="en">
|
|
1489
|
-
<head>
|
|
1490
|
-
<title>Page</title>
|
|
1491
|
-
</head>
|
|
1492
|
-
<body></body>
|
|
1493
|
-
</html>
|
|
1494
|
-
<html lang="en">
|
|
1495
|
-
<head>
|
|
1496
|
-
<title>{{ pageTitle }}</title>
|
|
1497
|
-
</head>
|
|
1498
|
-
<body></body>
|
|
1499
|
-
</html>
|
|
1500
|
-
```
|
|
1501
|
-
|
|
1502
|
-
---
|
|
1503
|
-
|
|
1504
|
-
## short-tui-imports
|
|
1505
|
-
|
|
1506
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1507
|
-
|
|
1508
|
-
In Angular decorator `imports` arrays, replaces full `TuiXxxComponent` / `TuiYyyDirective` names with their shorthand
|
|
1509
|
-
aliases (e.g. `TuiButton`). Also updates the corresponding `import` statement.
|
|
1510
|
-
|
|
1511
|
-
```ts
|
|
1512
|
-
// ❌ error
|
|
1513
|
-
import {TuiButtonDirective} from '@taiga-ui/core';
|
|
1514
|
-
|
|
1515
|
-
@Component({
|
|
1516
|
-
imports: [TuiButtonDirective],
|
|
1517
|
-
})
|
|
1518
|
-
|
|
1519
|
-
// ✅ after autofix
|
|
1520
|
-
import {TuiButton} from '@taiga-ui/core';
|
|
1521
|
-
|
|
1522
|
-
@Component({
|
|
1523
|
-
imports: [TuiButton],
|
|
1524
|
-
})
|
|
1525
|
-
```
|
|
1526
|
-
|
|
1527
|
-
```json
|
|
1528
|
-
{
|
|
1529
|
-
"@taiga-ui/experience-next/short-tui-imports": [
|
|
1530
|
-
"error",
|
|
1531
|
-
{
|
|
1532
|
-
"decorators": ["Component", "Directive", "NgModule", "Pipe"],
|
|
1533
|
-
"exceptions": [{"from": "TuiTextfieldOptionsDirective", "to": "TuiTextfield"}]
|
|
1534
|
-
}
|
|
1535
|
-
]
|
|
1536
|
-
}
|
|
1537
|
-
```
|
|
1538
|
-
|
|
1539
|
-
| Option | Type | Description |
|
|
1540
|
-
| ------------ | ------------------------------ | ---------------------------------------------------------- |
|
|
1541
|
-
| `decorators` | `string[]` | Decorator names to inspect (default: all Angular ones) |
|
|
1542
|
-
| `exceptions` | `{from: string, to: string}[]` | Explicit rename mappings that override the default pattern |
|
|
1543
|
-
|
|
1544
|
-
---
|
|
1545
|
-
|
|
1546
|
-
## single-line-class-property-spacing
|
|
1547
|
-
|
|
1548
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1549
|
-
|
|
1550
|
-
Keeps consecutive single-line field-like class members visually grouped, including `abstract` fields. A multiline field
|
|
1551
|
-
must be separated from neighboring field-like members with a blank line before it and, when another field follows, a
|
|
1552
|
-
blank line after it. `get` and `set` accessors always act as visual boundaries and must be separated from surrounding
|
|
1553
|
-
fields by a blank line. This removes noisy empty lines inside simple field groups without flattening longer, wrapped
|
|
1554
|
-
initializers or blending fields into accessors.
|
|
1555
|
-
|
|
1556
|
-
```ts
|
|
1557
|
-
// ❌ error
|
|
1558
|
-
class TuiEditorStarter {
|
|
1559
|
-
protected readonly template = import('./import/template.md?raw');
|
|
1560
|
-
|
|
1561
|
-
protected readonly component = import('./import/component.md?raw');
|
|
1562
|
-
protected readonly exampleIcons =
|
|
1563
|
-
import('./import/angular-to-long-long-long-long-long-long-long-text-for-prettier.json.md?raw');
|
|
1564
|
-
protected readonly isE2E = inject(TUI_IS_E2E);
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
// ✅ after autofix
|
|
1568
|
-
class TuiEditorStarter {
|
|
1569
|
-
protected readonly template = import('./import/template.md?raw');
|
|
1570
|
-
protected readonly component = import('./import/component.md?raw');
|
|
1571
|
-
|
|
1572
|
-
protected readonly exampleIcons =
|
|
1573
|
-
import('./import/angular-to-long-long-long-long-long-long-long-text-for-prettier.json.md?raw');
|
|
1574
|
-
|
|
1575
|
-
protected readonly isE2E = inject(TUI_IS_E2E);
|
|
1576
|
-
}
|
|
1577
|
-
```
|
|
1578
|
-
|
|
1579
|
-
```ts
|
|
1580
|
-
// ❌ error
|
|
1581
|
-
abstract class Example {
|
|
1582
|
-
protected readonly template = import('./template.md?raw');
|
|
1583
|
-
get component() {
|
|
1584
|
-
return this.template;
|
|
1585
|
-
}
|
|
1586
|
-
public abstract markdown: string;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
|
-
// ✅ after autofix
|
|
1590
|
-
abstract class Example {
|
|
1591
|
-
protected readonly template = import('./template.md?raw');
|
|
1592
|
-
|
|
1593
|
-
get component() {
|
|
1594
|
-
return this.template;
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
public abstract markdown: string;
|
|
1598
|
-
}
|
|
1599
|
-
```
|
|
1600
|
-
|
|
1601
|
-
---
|
|
1602
|
-
|
|
1603
|
-
## standalone-imports-sort
|
|
1604
|
-
|
|
1605
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1606
|
-
|
|
1607
|
-
Sorts the `imports` array inside Angular decorators (`@Component`, `@Directive`, `@NgModule`, `@Pipe`) alphabetically.
|
|
1608
|
-
Spread elements are placed after named identifiers.
|
|
1609
|
-
|
|
1610
|
-
```ts
|
|
1611
|
-
// ❌ error
|
|
1612
|
-
@Component({
|
|
1613
|
-
imports: [TuiButton, CommonModule, AsyncPipe],
|
|
1614
|
-
})
|
|
1615
|
-
|
|
1616
|
-
// ✅ after autofix
|
|
1617
|
-
@Component({
|
|
1618
|
-
imports: [AsyncPipe, CommonModule, TuiButton],
|
|
1619
|
-
})
|
|
1620
|
-
```
|
|
1621
|
-
|
|
1622
|
-
```json
|
|
1623
|
-
{
|
|
1624
|
-
"@taiga-ui/experience-next/standalone-imports-sort": [
|
|
1625
|
-
"error",
|
|
1626
|
-
{"decorators": ["Component", "Directive", "NgModule", "Pipe"]}
|
|
1627
|
-
]
|
|
1628
|
-
}
|
|
1629
|
-
```
|
|
1630
|
-
|
|
1631
|
-
---
|
|
1632
|
-
|
|
1633
|
-
## no-redundant-type-annotation
|
|
1634
|
-
|
|
1635
|
-
<sup>`✅ Recommended`</sup> <sup>`Fixable`</sup>
|
|
1636
|
-
|
|
1637
|
-
Disallow explicit type annotations on class properties and variable declarations when TypeScript can already infer the
|
|
1638
|
-
same type from the initializer. Requires type information (`parserOptions.project`).
|
|
1639
|
-
|
|
1640
|
-
Works well in combination with `unused-imports/no-unused-imports` or `@typescript-eslint/no-unused-vars`, which will
|
|
1641
|
-
then clean up any import that is no longer referenced after the annotation is removed.
|
|
1642
|
-
|
|
1643
|
-
```ts
|
|
1644
|
-
// ❌ error — type is already inferred from inject()
|
|
1645
|
-
private readonly options: TuiInputNumberOptions = inject(TUI_INPUT_NUMBER_OPTIONS);
|
|
1646
|
-
|
|
1647
|
-
// ✅ after autofix
|
|
1648
|
-
private readonly options = inject(TUI_INPUT_NUMBER_OPTIONS);
|
|
1649
|
-
```
|
|
1650
|
-
|
|
1651
|
-
```ts
|
|
1652
|
-
// ❌ error — variable declaration
|
|
1653
|
-
const service: MyService = inject(MyService);
|
|
1654
|
-
|
|
1655
|
-
// ✅ after autofix
|
|
1656
|
-
const service = inject(MyService);
|
|
1657
|
-
```
|
|
1658
|
-
|
|
1659
|
-
The rule does **not** report when the annotation intentionally widens or changes the type:
|
|
1660
|
-
|
|
1661
|
-
```ts
|
|
1662
|
-
// ✅ ok — annotation widens Dog to Animal
|
|
1663
|
-
x: Animal = new Dog();
|
|
1664
|
-
|
|
1665
|
-
// ✅ ok — annotation adds null to the union
|
|
1666
|
-
x: MyService | null = inject(MyService);
|
|
1667
|
-
```
|
|
1668
|
-
|
|
1669
|
-
The rule does **not** report when the annotation provides contextual typing that narrows an array literal to a tuple.
|
|
1670
|
-
Without the annotation TypeScript would infer `number[]` instead of the required tuple type, widening the type and
|
|
1671
|
-
breaking compilation:
|
|
1672
|
-
|
|
1673
|
-
```ts
|
|
1674
|
-
type SelectionRange = readonly [from: number, to: number];
|
|
1675
|
-
interface ElementState {
|
|
1676
|
-
readonly value: string;
|
|
1677
|
-
readonly selection: SelectionRange;
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
// ✅ ok — [0, 0] is inferred as SelectionRange only because of the annotation;
|
|
1681
|
-
// removing it would widen the type to ElementState | {value: string; selection: number[]}
|
|
1682
|
-
const state: ElementState = flag ? {value: '', selection: [0, 0]} : existingState;
|
|
1683
|
-
```
|
|
1684
|
-
|
|
1685
|
-
```json
|
|
1686
|
-
{
|
|
1687
|
-
"@taiga-ui/experience-next/no-redundant-type-annotation": ["error", {"ignoreTupleContextualTyping": true}]
|
|
1688
|
-
}
|
|
1689
|
-
```
|
|
1690
|
-
|
|
1691
|
-
| Option | Type | Default | Description |
|
|
1692
|
-
| ----------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------ |
|
|
1693
|
-
| `ignoreTupleContextualTyping` | `boolean` | `true` | Preserve annotations when they provide contextual typing that narrows an array literal to a tuple type |
|
|
1694
|
-
|
|
1695
|
-
---
|
|
1696
|
-
|
|
1697
|
-
## strict-tui-doc-example
|
|
1698
|
-
|
|
1699
|
-
<sup>`Taiga-specific`</sup> <sup>`Fixable`</sup>
|
|
1700
|
-
|
|
1701
|
-
Validates that properties of a `TuiDocExample`-typed object have keys matching known file-type names (`TypeScript`,
|
|
1702
|
-
`HTML`, `CSS`, `LESS`, `JavaScript`) and that the import path extension matches the key. Autofix corrects the import
|
|
1703
|
-
extension.
|
|
1704
|
-
|
|
1705
|
-
```ts
|
|
1706
|
-
// ❌ error — key says "TypeScript" but path has .html extension
|
|
1707
|
-
readonly example: TuiDocExample = {
|
|
1708
|
-
TypeScript: import('./example/index.html?raw'),
|
|
1709
|
-
};
|
|
1710
|
-
|
|
1711
|
-
// ✅ after autofix
|
|
1712
|
-
readonly example: TuiDocExample = {
|
|
1713
|
-
TypeScript: import('./example/index.ts?raw'),
|
|
1714
|
-
};
|
|
1715
|
-
```
|
|
40
|
+
| Rule | Description | ✅ | 🔧 | 💡 |
|
|
41
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --- | --- | --- |
|
|
42
|
+
| [array-as-const](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/array-as-const.md) | Exported array of class references should be marked with `as const` | | 🔧 | |
|
|
43
|
+
| [attrs-newline](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/attrs-newline.md) | Enforce one attribute per line when a start tag spans multiple lines | | 🔧 | |
|
|
44
|
+
| [class-property-naming](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/class-property-naming.md) | Enforce custom naming for class properties based on their type | | 🔧 | |
|
|
45
|
+
| [decorator-key-sort](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/decorator-key-sort.md) | Sorts the keys of the object passed to the `@Component/@Injectable/@NgModule/@Pipe` decorator | ✅ | 🔧 | |
|
|
46
|
+
| [element-newline](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/element-newline.md) | Require line breaks around block-level child nodes in HTML templates | | 🔧 | |
|
|
47
|
+
| [flat-exports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/flat-exports.md) | Spread nested arrays when exporting Angular entity collections | | 🔧 | |
|
|
48
|
+
| [host-attributes-sort](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/host-attributes-sort.md) | Sort Angular host metadata attributes using configurable attribute groups | ✅ | 🔧 | |
|
|
49
|
+
| [injection-token-description](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/injection-token-description.md) | Require `InjectionToken` descriptions to include the token name | ✅ | 🔧 | |
|
|
50
|
+
| [no-commonjs-import-patterns](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-commonjs-import-patterns.md) | Disallow legacy CommonJS interop import patterns | ✅ | | |
|
|
51
|
+
| [no-deep-imports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-deep-imports.md) | Disables deep imports of Taiga UI packages | ✅ | 🔧 | |
|
|
52
|
+
| [no-deep-imports-to-indexed-packages](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-deep-imports-to-indexed-packages.md) | Disallow deep imports from packages that expose an index.ts next to ng-package.json or package.json | ✅ | 🔧 | |
|
|
53
|
+
| [no-duplicate-attrs](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-duplicate-attrs.md) | Disallow duplicate attributes on the same HTML element | | | |
|
|
54
|
+
| [no-duplicate-id](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-duplicate-id.md) | Disallow duplicate static `id` values in HTML templates | | | |
|
|
55
|
+
| [no-duplicate-in-head](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-duplicate-in-head.md) | Disallow duplicate `title`, `base`, and key metadata tags inside `<head>` | | | |
|
|
56
|
+
| [no-fully-untracked-effect](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-fully-untracked-effect.md) | Disallow reactive callbacks where all signal reads are hidden inside `untracked()` | ✅ | | |
|
|
57
|
+
| [no-href-with-router-link](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-href-with-router-link.md) | Do not use href and routerLink attributes together on the same element | ✅ | 🔧 | |
|
|
58
|
+
| [no-import-assertions](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-import-assertions.md) | Replace legacy `assert { ... }` import assertions with `with { ... }` | ✅ | 🔧 | |
|
|
59
|
+
| [no-implicit-public](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-implicit-public.md) | Require explicit `public` modifier for class members and parameter properties | ✅ | 🔧 | |
|
|
60
|
+
| [no-infinite-loop](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-infinite-loop.md) | Disallow `while (true)` and `for` loops without an explicit condition | ✅ | | |
|
|
61
|
+
| [no-legacy-peer-deps](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-legacy-peer-deps.md) | Disallow `legacy-peer-deps=true` in `.npmrc` | ✅ | | |
|
|
62
|
+
| [no-obsolete-attrs](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-obsolete-attrs.md) | Disallow obsolete HTML attributes | | | |
|
|
63
|
+
| [no-obsolete-tags](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-obsolete-tags.md) | Disallow obsolete HTML tags | | | |
|
|
64
|
+
| [no-playwright-empty-fill](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-playwright-empty-fill.md) | Enforce `clear()` over `fill('')` in Playwright tests | ✅ | 🔧 | |
|
|
65
|
+
| [no-project-as-in-ng-template](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-project-as-in-ng-template.md) | `ngProjectAs` has no effect inside `<ng-template>` or dynamic outlets | ✅ | | |
|
|
66
|
+
| [no-restricted-attr-values](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-restricted-attr-values.md) | Disallow configured attribute values in Angular templates | | | |
|
|
67
|
+
| [no-redundant-type-annotation](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-redundant-type-annotation.md) | Disallow redundant type annotations when the type is already inferred from the initializer | ✅ | 🔧 | |
|
|
68
|
+
| [no-side-effects-in-computed](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-side-effects-in-computed.md) | Disallow side effects and effectful helper calls inside Angular `computed()` callbacks | ✅ | | |
|
|
69
|
+
| [no-signal-reads-after-await-in-reactive-context](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-signal-reads-after-await-in-reactive-context.md) | Disallow bare signal reads after `await` inside reactive callbacks | ✅ | | |
|
|
70
|
+
| [no-string-literal-concat](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-string-literal-concat.md) | Disallow string literal concatenation; merge adjacent literals into one | ✅ | 🔧 | |
|
|
71
|
+
| [no-untracked-outside-reactive-context](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-untracked-outside-reactive-context.md) | Disallow `untracked()` outside reactive callbacks, except explicit post-`await` snapshots | ✅ | 🔧 | |
|
|
72
|
+
| [no-useless-untracked](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-useless-untracked.md) | Disallow provably useless `untracked()` wrappers in reactive callbacks | ✅ | 🔧 | |
|
|
73
|
+
| [object-single-line](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/object-single-line.md) | Enforce single-line formatting for single-property objects when it fits `printWidth` | ✅ | 🔧 | |
|
|
74
|
+
| [prefer-combined-if-control-flow](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-combined-if-control-flow.md) | Combine consecutive `if` statements that use the same `return`, `break`, `continue`, or `throw` | ✅ | 🔧 | |
|
|
75
|
+
| [prefer-deep-imports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-deep-imports.md) | Allow deep imports of Taiga UI packages | | 🔧 | |
|
|
76
|
+
| [prefer-multi-arg-push](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-multi-arg-push.md) | Combine consecutive `.push()` calls on the same array into a single multi-argument call | ✅ | 🔧 | |
|
|
77
|
+
| [prefer-namespace-keyword](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-namespace-keyword.md) | Replace `module Foo {}` with `namespace Foo {}` for TypeScript namespace declarations | ✅ | 🔧 | |
|
|
78
|
+
| [prefer-untracked-incidental-signal-reads](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-untracked-incidental-signal-reads.md) | Wrap likely-incidental signal reads with `untracked()` in reactive callbacks | ✅ | 🔧 | |
|
|
79
|
+
| [prefer-untracked-signal-getter](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-untracked-signal-getter.md) | Prefer `untracked(signalGetter)` over `untracked(() => signalGetter())` for a single signal getter | ✅ | 🔧 | |
|
|
80
|
+
| [quotes](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/quotes.md) | Enforce double quotes around HTML attribute values | | 🔧 | |
|
|
81
|
+
| [require-doctype](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/require-doctype.md) | Require `<!DOCTYPE html>` at the top of HTML documents | | 🔧 | |
|
|
82
|
+
| [require-img-alt](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/require-img-alt.md) | Require `alt` on `<img>` elements, including Angular attribute bindings | | | |
|
|
83
|
+
| [require-lang](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/require-lang.md) | Require a non-empty `lang` attribute on `<html>` | | | |
|
|
84
|
+
| [require-li-container](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/require-li-container.md) | Require `<li>` to be nested inside `<ul>`, `<ol>`, or `<menu>` | | | |
|
|
85
|
+
| [require-title](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/require-title.md) | Require a non-empty `<title>` inside `<head>` | | | |
|
|
86
|
+
| [short-tui-imports](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/short-tui-imports.md) | Shorten TuiXxxComponent / TuiYyyDirective in Angular metadata | ✅ | 🔧 | |
|
|
87
|
+
| [single-line-class-property-spacing](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/single-line-class-property-spacing.md) | Group consecutive single-line class properties and separate multiline ones with a blank line | ✅ | 🔧 | |
|
|
88
|
+
| [standalone-imports-sort](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/standalone-imports-sort.md) | Auto sort names inside Angular decorators | ✅ | 🔧 | |
|
|
89
|
+
| [strict-tui-doc-example](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/strict-tui-doc-example.md) | If you use the addon-doc, there will be a hint that you are importing something incorrectly | | 🔧 | |
|