@taiga-ui/eslint-plugin-experience-next 0.519.0 → 0.520.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 CHANGED
@@ -70,63 +70,64 @@ from third-party plugins. The exact severities and file globs live in
70
70
  - 🔧 = fixable
71
71
  - 💡 = has suggestions
72
72
 
73
- | Rule | Description | ✅ | 🔧 | 💡 |
74
- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --- | --- | --- |
75
- | [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` | | 🔧 | |
76
- | [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 | ✅ | 🔧 | |
77
- | [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 | | 🔧 | |
78
- | [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 | ✅ | 🔧 | |
79
- | [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 | ✅ | 🔧 | |
80
- | [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 | | 🔧 | |
81
- | [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 | ✅ | 🔧 | |
82
- | [html-logical-properties](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/html-logical-properties.md) | Enforce logical CSS properties over directional ones in Angular template style bindings | ✅ | 🔧 | |
83
- | [import-integrity](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/import-integrity.md) | Fast import default, namespace, cycle, duplicate, named-as-default, self-import, and path checks | ✅ | 🔧 | |
84
- | [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 | ✅ | 🔧 | |
85
- | [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 | ✅ | | |
86
- | [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 | ✅ | 🔧 | |
87
- | [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 | ✅ | 🔧 | |
88
- | [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 | ✅ | | |
89
- | [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 | ✅ | | |
90
- | [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>` | ✅ | | |
91
- | [no-empty-style-metadata](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-empty-style-metadata.md) | Remove empty Angular component style metadata | ✅ | 🔧 | |
92
- | [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()` | ✅ | | |
93
- | [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 | ✅ | 🔧 | |
94
- | [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 { ... }` | ✅ | 🔧 | |
95
- | [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 | ✅ | 🔧 | |
96
- | [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 | ✅ | | |
97
- | [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` | ✅ | | |
98
- | [no-nested-interactive](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-nested-interactive.md) | Disallow interactive HTML elements nested inside other interactive elements | ✅ | | |
99
- | [no-nested-ternary-in-template](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-nested-ternary-in-template.md) | Disallow nested ternary expressions in Angular 19+ templates | ✅ | 🔧 | |
100
- | [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 | ✅ | | |
101
- | [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 | ✅ | | |
102
- | [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 | ✅ | 🔧 | |
103
- | [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 | ✅ | | |
104
- | [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 | | | |
105
- | [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 | ✅ | 🔧 | |
106
- | [no-repeated-signal-in-conditional](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-repeated-signal-in-conditional.md) | Disallow reading the same nullable Angular signal more than once in a conditional expression | ✅ | 🔧 | |
107
- | [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 | ✅ | | |
108
- | [no-signal-outside-class](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-signal-outside-class.md) | Disallow class properties that reference a module-scope Angular signal | ✅ | | |
109
- | [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 | ✅ | | |
110
- | [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 | ✅ | 🔧 | |
111
- | [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 | ✅ | 🔧 | |
112
- | [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 | ✅ | 🔧 | |
113
- | [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` | ✅ | 🔧 | |
114
- | [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` | ✅ | 🔧 | |
115
- | [prefer-conditional-return](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-conditional-return.md) | Prefer a conditional return over an adjacent `if` branch and fallback `return` | ✅ | 🔧 | |
116
- | [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 | | 🔧 | |
117
- | [prefer-loose-null-check](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-loose-null-check.md) | Prefer loose null checks over paired strict comparisons against `null` and `undefined` | ✅ | 🔧 | |
118
- | [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 | ✅ | 🔧 | |
119
- | [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 | ✅ | 🔧 | |
120
- | [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 | ✅ | 🔧 | |
121
- | [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 | ✅ | 🔧 | |
122
- | [quotes](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/quotes.md) | Enforce double quotes around HTML attribute values | ✅ | 🔧 | |
123
- | [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 | ✅ | 🔧 | |
124
- | [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 | ✅ | | |
125
- | [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>` | ✅ | | |
126
- | [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>` | ✅ | | |
127
- | [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>` | ✅ | | |
128
- | [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 | ✅ | 🔧 | |
129
- | [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 | ✅ | 🔧 | |
130
- | [single-line-variable-spacing](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/single-line-variable-spacing.md) | Group consecutive single-line variables and separate multiline ones with a blank line | ✅ | 🔧 | |
131
- | [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 | ✅ | 🔧 | |
132
- | [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 | | 🔧 | |
73
+ | Rule | Description | ✅ | 🔧 | 💡 |
74
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --- | --- | --- |
75
+ | [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` | | 🔧 | |
76
+ | [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 | ✅ | 🔧 | |
77
+ | [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 | | 🔧 | |
78
+ | [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 | ✅ | 🔧 | |
79
+ | [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 | ✅ | 🔧 | |
80
+ | [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 | | 🔧 | |
81
+ | [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 | ✅ | 🔧 | |
82
+ | [html-logical-properties](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/html-logical-properties.md) | Enforce logical CSS properties over directional ones in Angular template style bindings | ✅ | 🔧 | |
83
+ | [import-integrity](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/import-integrity.md) | Fast import default, namespace, cycle, duplicate, named-as-default, self-import, and path checks | ✅ | 🔧 | |
84
+ | [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 | ✅ | 🔧 | |
85
+ | [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 | ✅ | | |
86
+ | [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 | ✅ | 🔧 | |
87
+ | [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 | ✅ | 🔧 | |
88
+ | [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 | ✅ | | |
89
+ | [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 | ✅ | | |
90
+ | [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>` | ✅ | | |
91
+ | [no-empty-style-metadata](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-empty-style-metadata.md) | Remove empty Angular component style metadata | ✅ | 🔧 | |
92
+ | [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()` | ✅ | | |
93
+ | [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 | ✅ | 🔧 | |
94
+ | [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 { ... }` | ✅ | 🔧 | |
95
+ | [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 | ✅ | 🔧 | |
96
+ | [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 | ✅ | | |
97
+ | [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` | ✅ | | |
98
+ | [no-nested-interactive](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-nested-interactive.md) | Disallow interactive HTML elements nested inside other interactive elements | ✅ | | |
99
+ | [no-nested-ternary-in-template](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-nested-ternary-in-template.md) | Disallow nested ternary expressions in Angular 19+ templates | ✅ | 🔧 | |
100
+ | [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 | ✅ | | |
101
+ | [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 | ✅ | | |
102
+ | [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 | ✅ | 🔧 | |
103
+ | [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 | ✅ | | |
104
+ | [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 | | | |
105
+ | [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 | ✅ | 🔧 | |
106
+ | [no-repeated-signal-in-conditional](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-repeated-signal-in-conditional.md) | Disallow reading the same nullable Angular signal more than once in a conditional expression | ✅ | 🔧 | |
107
+ | [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 | ✅ | | |
108
+ | [no-signal-outside-class](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/no-signal-outside-class.md) | Disallow class properties that reference a module-scope Angular signal | ✅ | | |
109
+ | [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 | ✅ | | |
110
+ | [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 | ✅ | 🔧 | |
111
+ | [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 | ✅ | 🔧 | |
112
+ | [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 | ✅ | 🔧 | |
113
+ | [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` | ✅ | 🔧 | |
114
+ | [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` | ✅ | 🔧 | |
115
+ | [prefer-conditional-return](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-conditional-return.md) | Prefer a conditional return over an adjacent `if` branch and fallback `return` | ✅ | 🔧 | |
116
+ | [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 | | 🔧 | |
117
+ | [prefer-loose-null-check](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/prefer-loose-null-check.md) | Prefer loose null checks over paired strict comparisons against `null` and `undefined` | ✅ | 🔧 | |
118
+ | [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 | ✅ | 🔧 | |
119
+ | [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 | ✅ | 🔧 | |
120
+ | [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 | ✅ | 🔧 | |
121
+ | [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 | ✅ | 🔧 | |
122
+ | [quotes](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/quotes.md) | Enforce double quotes around HTML attribute values | ✅ | 🔧 | |
123
+ | [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 | ✅ | 🔧 | |
124
+ | [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 | ✅ | | |
125
+ | [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>` | ✅ | | |
126
+ | [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>` | ✅ | | |
127
+ | [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>` | ✅ | | |
128
+ | [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 | ✅ | 🔧 | |
129
+ | [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 | ✅ | 🔧 | |
130
+ | [single-line-let-spacing](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/single-line-let-spacing.md) | Group consecutive single-line @let declarations and separate multiline ones and interpolations with blank lines | ✅ | 🔧 | |
131
+ | [single-line-variable-spacing](https://github.com/taiga-family/toolkit/tree/main/projects/eslint-plugin-experience-next/docs/single-line-variable-spacing.md) | Group consecutive single-line variables and separate multiline ones with a blank line | ✅ | 🔧 | |
132
+ | [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 | | 🔧 | |
133
+ | [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 | | 🔧 | |
package/index.d.ts CHANGED
@@ -202,6 +202,9 @@ declare const plugin: {
202
202
  'single-line-class-property-spacing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingBlankLineAfterMultilineProperty" | "missingBlankLineAroundAccessor" | "missingBlankLineBeforeMultilineProperty" | "unexpectedBlankLineBeforeNextSingleLineField", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
203
203
  name: string;
204
204
  };
205
+ 'single-line-let-spacing': import("eslint").Rule.RuleModule & {
206
+ name: string;
207
+ };
205
208
  'single-line-variable-spacing': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingBlankLineAfterMultilineVariable" | "missingBlankLineBeforeMultilineVariable" | "missingBlankLineBetweenVariableGroups" | "unexpectedBlankLineBeforeNextSingleLineVariable", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener> & {
206
209
  name: string;
207
210
  };
package/index.esm.js CHANGED
@@ -1321,6 +1321,7 @@ var recommended = defineConfig([
1321
1321
  '@taiga-ui/experience-next/require-lang': 'error',
1322
1322
  '@taiga-ui/experience-next/require-li-container': 'error',
1323
1323
  '@taiga-ui/experience-next/require-title': 'error',
1324
+ '@taiga-ui/experience-next/single-line-let-spacing': angularVersion >= modernAngularRules.templateLet ? 'error' : 'off',
1324
1325
  },
1325
1326
  },
1326
1327
  {
@@ -46272,7 +46273,7 @@ function buildMultilineStartTag(node, sourceText) {
46272
46273
  closing,
46273
46274
  ].join('\n');
46274
46275
  }
46275
- const rule$V = createRule({
46276
+ const rule$W = createRule({
46276
46277
  name: 'attrs-newline',
46277
46278
  rule: {
46278
46279
  create(context) {
@@ -46452,7 +46453,7 @@ function sameOrder(a, b) {
46452
46453
  return a.length === b.length && a.every((value, index) => value === b[index]);
46453
46454
  }
46454
46455
 
46455
- const rule$U = createRule({
46456
+ const rule$V = createRule({
46456
46457
  create(context, [order]) {
46457
46458
  const decorators = new Set(Object.keys(order));
46458
46459
  return {
@@ -46588,7 +46589,7 @@ function getNodeLabel(node) {
46588
46589
  }
46589
46590
  return node instanceof dist$4.TmplAstBoundText ? 'binding' : 'text';
46590
46591
  }
46591
- const rule$T = createRule({
46592
+ const rule$U = createRule({
46592
46593
  name: 'element-newline',
46593
46594
  rule: {
46594
46595
  create(context) {
@@ -46727,7 +46728,7 @@ const PRESETS = {
46727
46728
  $VUE: ['$CLASS', '$ID', '$VUE_ATTRIBUTE'],
46728
46729
  $VUE_ATTRIBUTE: /^v-/,
46729
46730
  };
46730
- const rule$S = createRule({
46731
+ const rule$T = createRule({
46731
46732
  create(context, [options]) {
46732
46733
  const sourceCode = context.sourceCode;
46733
46734
  const settings = {
@@ -47058,7 +47059,7 @@ const config$4 = {
47058
47059
  type: 'suggestion',
47059
47060
  },
47060
47061
  };
47061
- const rule$R = createRule({
47062
+ const rule$S = createRule({
47062
47063
  name: 'html-logical-properties',
47063
47064
  rule: config$4,
47064
47065
  });
@@ -248300,7 +248301,7 @@ function isImportUsedOnlyAsAngularDiFirstArg(node, sourceCode) {
248300
248301
  }
248301
248302
  return hasSafeRuntimeUsage;
248302
248303
  }
248303
- const rule$Q = createRule({
248304
+ const rule$R = createRule({
248304
248305
  create(context) {
248305
248306
  const { checker, esTreeNodeToTSNodeMap, sourceCode, tsProgram } = getTypeAwareRuleContext(context);
248306
248307
  const checkCycles = context.options[0]?.checkCycles ?? true;
@@ -249025,7 +249026,7 @@ function getNgDevModeDeclarationFix(program, fixer) {
249025
249026
  ? fixer.insertTextBefore(firstStatement, 'declare const ngDevMode: boolean;\n\n')
249026
249027
  : fixer.insertTextBeforeRange([0, 0], 'declare const ngDevMode: boolean;\n');
249027
249028
  }
249028
- const rule$P = createRule({
249029
+ const rule$Q = createRule({
249029
249030
  create(context) {
249030
249031
  const { sourceCode } = context;
249031
249032
  const program = sourceCode.ast;
@@ -249074,7 +249075,7 @@ const rule$P = createRule({
249074
249075
  name: 'injection-token-description',
249075
249076
  });
249076
249077
 
249077
- const rule$O = createRule({
249078
+ const rule$P = createRule({
249078
249079
  create(context) {
249079
249080
  const { sourceCode } = context;
249080
249081
  const namespaceImports = new Map();
@@ -249169,7 +249170,7 @@ const DEFAULT_OPTIONS = {
249169
249170
  importDeclaration: '^@taiga-ui*',
249170
249171
  projectName: String.raw `(?<=^@taiga-ui/)([-\w]+)`,
249171
249172
  };
249172
- const rule$N = createRule({
249173
+ const rule$O = createRule({
249173
249174
  create(context) {
249174
249175
  const { currentProject, deepImport, ignoreImports, importDeclaration, projectName, } = { ...DEFAULT_OPTIONS, ...context.options[0] };
249175
249176
  const hasNonCodeExtension = (source) => {
@@ -249261,7 +249262,7 @@ const nearestFileUpCache = new Map();
249261
249262
  const markerCache = new Map();
249262
249263
  const indexFileCache = new Map();
249263
249264
  const indexExportsCache = new Map();
249264
- const rule$M = createRule({
249265
+ const rule$N = createRule({
249265
249266
  create(context) {
249266
249267
  const parserServices = dist$3.ESLintUtils.getParserServices(context);
249267
249268
  const program = parserServices.program;
@@ -249455,13 +249456,13 @@ const noDuplicateAttributesRule = angular.templatePlugin.rules?.['no-duplicate-a
249455
249456
  if (!noDuplicateAttributesRule) {
249456
249457
  throw new Error('angular-eslint template rule "no-duplicate-attributes" is not available');
249457
249458
  }
249458
- const rule$L = createRule({
249459
+ const rule$M = createRule({
249459
249460
  name: 'no-duplicate-attrs',
249460
249461
  rule: noDuplicateAttributesRule,
249461
249462
  });
249462
249463
 
249463
249464
  const MESSAGE_ID$e = 'duplicateId';
249464
- const rule$K = createRule({
249465
+ const rule$L = createRule({
249465
249466
  name: 'no-duplicate-id',
249466
249467
  rule: {
249467
249468
  create(context) {
@@ -249519,7 +249520,7 @@ function getTrackingKey(node) {
249519
249520
  ? 'link[rel=canonical]'
249520
249521
  : null;
249521
249522
  }
249522
- const rule$J = createRule({
249523
+ const rule$K = createRule({
249523
249524
  name: 'no-duplicate-in-head',
249524
249525
  rule: {
249525
249526
  create(context) {
@@ -249575,7 +249576,7 @@ const rule$J = createRule({
249575
249576
  });
249576
249577
 
249577
249578
  const COMPONENT_DECORATORS = new Set(['Component']);
249578
- const rule$I = createRule({
249579
+ const rule$J = createRule({
249579
249580
  create(context) {
249580
249581
  const { sourceCode } = context;
249581
249582
  return {
@@ -250223,7 +250224,7 @@ const ANGULAR_SIGNALS_UNTRACKED_GUIDE_URL = 'https://angular.dev/guide/signals#r
250223
250224
  const ANGULAR_SIGNALS_ASYNC_GUIDE_URL = 'https://angular.dev/guide/signals#reactive-context-and-async-operations';
250224
250225
  const createUntrackedRule = createRule;
250225
250226
 
250226
- const rule$H = createUntrackedRule({
250227
+ const rule$I = createUntrackedRule({
250227
250228
  create(context) {
250228
250229
  const { checker, esTreeNodeToTSNodeMap, program } = getTypeAwareRuleContext(context);
250229
250230
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -250296,7 +250297,7 @@ const config$3 = {
250296
250297
  type: 'problem',
250297
250298
  },
250298
250299
  };
250299
- const rule$G = createRule({
250300
+ const rule$H = createRule({
250300
250301
  name: 'no-href-with-router-link',
250301
250302
  rule: config$3,
250302
250303
  });
@@ -250357,7 +250358,7 @@ function getScopeRoot(node) {
250357
250358
  return (findAncestor(node, (ancestor) => ancestor.type === dist$3.AST_NODE_TYPES.Program || isFunctionLike(ancestor)) ?? node);
250358
250359
  }
250359
250360
 
250360
- const rule$F = createRule({
250361
+ const rule$G = createRule({
250361
250362
  create(context) {
250362
250363
  const checkImplicitPublic = (node) => {
250363
250364
  const classRef = getEnclosingClass(node);
@@ -250419,7 +250420,7 @@ const rule$F = createRule({
250419
250420
  name: 'no-implicit-public',
250420
250421
  });
250421
250422
 
250422
- const rule$E = createRule({
250423
+ const rule$F = createRule({
250423
250424
  create(context) {
250424
250425
  const { sourceCode } = context;
250425
250426
  return {
@@ -250475,7 +250476,7 @@ function isInfiniteLoopLiteral(node) {
250475
250476
  function isInfiniteLoopTest(test) {
250476
250477
  return test == null || isInfiniteLoopLiteral(test);
250477
250478
  }
250478
- const rule$D = createRule({
250479
+ const rule$E = createRule({
250479
250480
  create(context) {
250480
250481
  return {
250481
250482
  DoWhileStatement(node) {
@@ -250520,7 +250521,7 @@ const rule$D = createRule({
250520
250521
  });
250521
250522
 
250522
250523
  const LEGACY_PEER_DEPS_PATTERN = /^legacy-peer-deps\s*=\s*true$/i;
250523
- const rule$C = createRule({
250524
+ const rule$D = createRule({
250524
250525
  create(context) {
250525
250526
  return {
250526
250527
  Program(node) {
@@ -250612,7 +250613,7 @@ function getAvailableLabelParent(stack, node, labelsWithControl) {
250612
250613
  ? parent
250613
250614
  : null;
250614
250615
  }
250615
- const rule$B = createRule({
250616
+ const rule$C = createRule({
250616
250617
  name: 'no-nested-interactive',
250617
250618
  rule: {
250618
250619
  create(context) {
@@ -250881,7 +250882,7 @@ function createLetFixes(node, baseName, unavailableNames, text) {
250881
250882
  reference: name,
250882
250883
  };
250883
250884
  }
250884
- const rule$A = createRule({
250885
+ const rule$B = createRule({
250885
250886
  name: 'no-nested-ternary-in-template',
250886
250887
  rule: {
250887
250888
  create(context) {
@@ -251418,7 +251419,7 @@ const OBSOLETE_HTML_ATTRS = {
251418
251419
  };
251419
251420
 
251420
251421
  const MESSAGE_ID$9 = 'obsolete';
251421
- const rule$z = createRule({
251422
+ const rule$A = createRule({
251422
251423
  name: 'no-obsolete-attrs',
251423
251424
  rule: {
251424
251425
  create(context) {
@@ -251496,7 +251497,7 @@ const OBSOLETE_HTML_TAGS = new Set([
251496
251497
  ]);
251497
251498
 
251498
251499
  const MESSAGE_ID$8 = 'unexpected';
251499
- const rule$y = createRule({
251500
+ const rule$z = createRule({
251500
251501
  name: 'no-obsolete-tags',
251501
251502
  rule: {
251502
251503
  create(context) {
@@ -251523,7 +251524,7 @@ const rule$y = createRule({
251523
251524
  },
251524
251525
  });
251525
251526
 
251526
- const rule$x = createRule({
251527
+ const rule$y = createRule({
251527
251528
  create(context) {
251528
251529
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
251529
251530
  return {
@@ -251667,7 +251668,7 @@ const config$2 = {
251667
251668
  type: 'problem',
251668
251669
  },
251669
251670
  };
251670
- const rule$w = createRule({
251671
+ const rule$x = createRule({
251671
251672
  name: 'no-project-as-in-ng-template',
251672
251673
  rule: config$2,
251673
251674
  });
@@ -251704,7 +251705,7 @@ function collectArrayExpressions(node) {
251704
251705
  }
251705
251706
  return result;
251706
251707
  }
251707
- const rule$v = createRule({
251708
+ const rule$w = createRule({
251708
251709
  create(context) {
251709
251710
  const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
251710
251711
  const ignoreTupleContextualTyping = context.options[0]?.ignoreTupleContextualTyping ?? true;
@@ -251902,7 +251903,7 @@ function isOptionalMemberReceiver(call) {
251902
251903
  parent.object === current &&
251903
251904
  parent.optional);
251904
251905
  }
251905
- const rule$u = createRule({
251906
+ const rule$v = createRule({
251906
251907
  create(context) {
251907
251908
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
251908
251909
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252347,7 +252348,7 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
252347
252348
  return;
252348
252349
  });
252349
252350
  }
252350
- const rule$t = createRule({
252351
+ const rule$u = createRule({
252351
252352
  create(context) {
252352
252353
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
252353
252354
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252428,7 +252429,7 @@ function collectModuleScopeSignalNames(program, factoryNames) {
252428
252429
  }
252429
252430
  return names;
252430
252431
  }
252431
- const rule$s = createRule({
252432
+ const rule$t = createRule({
252432
252433
  create(context) {
252433
252434
  const program = context.sourceCode.ast;
252434
252435
  const factoryNames = new Set();
@@ -252451,6 +252452,16 @@ const rule$s = createRule({
252451
252452
  !moduleScopeSignals.has(value.name)) {
252452
252453
  return;
252453
252454
  }
252455
+ const variable = getResolvedVariable(context.sourceCode, value);
252456
+ if (!variable) {
252457
+ return;
252458
+ }
252459
+ const enclosingClass = getEnclosingClass(node);
252460
+ const isUsedElsewhere = variable.references.some((ref) => ref.isRead() &&
252461
+ getEnclosingClass(ref.identifier) !== enclosingClass);
252462
+ if (isUsedElsewhere) {
252463
+ return;
252464
+ }
252454
252465
  context.report({
252455
252466
  data: { name: value.name },
252456
252467
  messageId: 'noSignalOutsideClass',
@@ -252472,7 +252483,7 @@ const rule$s = createRule({
252472
252483
  name: 'no-signal-outside-class',
252473
252484
  });
252474
252485
 
252475
- const rule$r = createUntrackedRule({
252486
+ const rule$s = createUntrackedRule({
252476
252487
  create(context) {
252477
252488
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252478
252489
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252564,7 +252575,7 @@ function templateContent(template, renderExpr) {
252564
252575
  : ''}`)
252565
252576
  .join('');
252566
252577
  }
252567
- const rule$q = createRule({
252578
+ const rule$r = createRule({
252568
252579
  create(context) {
252569
252580
  const { sourceCode } = context;
252570
252581
  let parserServices = null;
@@ -252886,7 +252897,7 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
252886
252897
  ? text
252887
252898
  : dedent(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
252888
252899
  }
252889
- const rule$p = createUntrackedRule({
252900
+ const rule$q = createUntrackedRule({
252890
252901
  create(context) {
252891
252902
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252892
252903
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -253015,7 +253026,7 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
253015
253026
  });
253016
253027
  return found;
253017
253028
  }
253018
- const rule$o = createUntrackedRule({
253029
+ const rule$p = createUntrackedRule({
253019
253030
  create(context) {
253020
253031
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
253021
253032
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -253097,7 +253108,7 @@ const rule$o = createUntrackedRule({
253097
253108
  name: 'no-useless-untracked',
253098
253109
  });
253099
253110
 
253100
- const rule$n = createRule({
253111
+ const rule$o = createRule({
253101
253112
  create(context, [{ printWidth }]) {
253102
253113
  const sourceCode = context.sourceCode;
253103
253114
  const getLineEndIndex = (lineStartIndex) => {
@@ -253411,7 +253422,7 @@ function renderTest(node, sourceCode) {
253411
253422
  const text = sourceCode.getText(node);
253412
253423
  return needsParenthesesInOrChain(node) ? `(${text})` : text;
253413
253424
  }
253414
- const rule$m = createRule({
253425
+ const rule$n = createRule({
253415
253426
  create(context) {
253416
253427
  const { sourceCode } = context;
253417
253428
  function checkBody(statements) {
@@ -253708,7 +253719,7 @@ function renderBooleanTestReturn(ifStatement, sourceCode, strategy) {
253708
253719
  const indent = getStatementIndent(ifStatement, sourceCode);
253709
253720
  return `return (\n${indent} ${test}\n${indent});`;
253710
253721
  }
253711
- const rule$l = createRule({
253722
+ const rule$m = createRule({
253712
253723
  create(context) {
253713
253724
  const { sourceCode } = context;
253714
253725
  let parserServices = null;
@@ -253851,7 +253862,7 @@ function findLooseNullCheckMatch(parsedChecks) {
253851
253862
  }
253852
253863
  return null;
253853
253864
  }
253854
- const rule$k = createRule({
253865
+ const rule$l = createRule({
253855
253866
  create(context) {
253856
253867
  const getText = (n) => context.sourceCode.getText(n);
253857
253868
  return {
@@ -253909,7 +253920,7 @@ function getPushCall(node) {
253909
253920
  ? null
253910
253921
  : call;
253911
253922
  }
253912
- const rule$j = createRule({
253923
+ const rule$k = createRule({
253913
253924
  create(context) {
253914
253925
  const { sourceCode } = context;
253915
253926
  function checkBody(statements) {
@@ -254004,7 +254015,7 @@ function getModuleKeywordToken(sourceCode, node) {
254004
254015
  ? (sourceCode.getTokenAfter(firstToken) ?? null)
254005
254016
  : firstToken;
254006
254017
  }
254007
- const rule$i = createRule({
254018
+ const rule$j = createRule({
254008
254019
  create(context) {
254009
254020
  const { sourceCode } = context;
254010
254021
  return {
@@ -254271,7 +254282,7 @@ function collectSuspiciousReads(scope, checker, esTreeNodeToTSNodeMap, tsNodeToE
254271
254282
  });
254272
254283
  return [...suspicious.values()];
254273
254284
  }
254274
- const rule$h = createUntrackedRule({
254285
+ const rule$i = createUntrackedRule({
254275
254286
  create(context) {
254276
254287
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
254277
254288
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -254357,7 +254368,7 @@ function getWrappedSignalGetter(node, checker, esTreeNodeToTSNodeMap) {
254357
254368
  }
254358
254369
  return isSignalType(getter, checker, esTreeNodeToTSNodeMap) ? body.callee : null;
254359
254370
  }
254360
- const rule$g = createUntrackedRule({
254371
+ const rule$h = createUntrackedRule({
254361
254372
  create(context) {
254362
254373
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
254363
254374
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -254403,7 +254414,7 @@ const DOUBLE_QUOTE = '"';
254403
254414
  function isQuotableAttribute(attr) {
254404
254415
  return 'sourceSpan' in attr;
254405
254416
  }
254406
- const rule$f = createRule({
254417
+ const rule$g = createRule({
254407
254418
  name: 'quotes',
254408
254419
  rule: {
254409
254420
  create(context) {
@@ -254488,7 +254499,7 @@ const rule$f = createRule({
254488
254499
 
254489
254500
  const MESSAGE_ID$6 = 'missing';
254490
254501
  const DOCTYPE_REGEXP = /^\s*<!doctype html>/i;
254491
- const rule$e = createRule({
254502
+ const rule$f = createRule({
254492
254503
  name: 'require-doctype',
254493
254504
  rule: {
254494
254505
  create(context) {
@@ -254522,7 +254533,7 @@ const rule$e = createRule({
254522
254533
  });
254523
254534
 
254524
254535
  const MESSAGE_ID$5 = 'missingAlt';
254525
- const rule$d = createRule({
254536
+ const rule$e = createRule({
254526
254537
  name: 'require-img-alt',
254527
254538
  rule: {
254528
254539
  create(context) {
@@ -254552,7 +254563,7 @@ const MESSAGE_IDS$1 = {
254552
254563
  EMPTY: 'empty',
254553
254564
  MISSING: 'missing',
254554
254565
  };
254555
- const rule$c = createRule({
254566
+ const rule$d = createRule({
254556
254567
  name: 'require-lang',
254557
254568
  rule: {
254558
254569
  create(context) {
@@ -254614,7 +254625,7 @@ function getClosestParentElement(node) {
254614
254625
  }
254615
254626
  return null;
254616
254627
  }
254617
- const rule$b = createRule({
254628
+ const rule$c = createRule({
254618
254629
  name: 'require-li-container',
254619
254630
  rule: {
254620
254631
  create(context) {
@@ -254662,7 +254673,7 @@ function hasMeaningfulTitleContent(node) {
254662
254673
  (typeof value === 'string' && value.trim().length > 0));
254663
254674
  });
254664
254675
  }
254665
- const rule$a = createRule({
254676
+ const rule$b = createRule({
254666
254677
  name: 'require-title',
254667
254678
  rule: {
254668
254679
  create(context) {
@@ -254751,7 +254762,7 @@ const DEFAULT_EXCEPTIONS = [
254751
254762
  { from: 'TuiIslandDirective', to: 'TuiIsland' },
254752
254763
  { from: 'TuiTableBarsHostComponent', to: 'TuiTableBarsHost' },
254753
254764
  ];
254754
- const rule$9 = createRule({
254765
+ const rule$a = createRule({
254755
254766
  create(context, [{ decorators = DEFAULT_DECORATORS, exceptions = DEFAULT_EXCEPTIONS }]) {
254756
254767
  const sourceCode = context.getSourceCode();
254757
254768
  const allowedDecorators = new Set(decorators);
@@ -254910,7 +254921,7 @@ function isRelevantSpacingClassMember(member) {
254910
254921
  return isFieldLikeMember(member) || isAccessorMember(member);
254911
254922
  }
254912
254923
 
254913
- const rule$8 = createRule({
254924
+ const rule$9 = createRule({
254914
254925
  create(context) {
254915
254926
  const sourceCode = context.sourceCode;
254916
254927
  return {
@@ -254994,6 +255005,150 @@ const rule$8 = createRule({
254994
255005
  name: 'single-line-class-property-spacing',
254995
255006
  });
254996
255007
 
255008
+ const rule$8 = createRule({
255009
+ name: 'single-line-let-spacing',
255010
+ rule: {
255011
+ create(context) {
255012
+ const { sourceCode } = context;
255013
+ const sourceText = sourceCode.getText();
255014
+ function buildReplacement(betweenText, nextLine, blankLineCount) {
255015
+ const lineBreak = getLineBreak(betweenText);
255016
+ const indentation = getLeadingIndentation(sourceCode.lines[nextLine] ?? '');
255017
+ return `;${lineBreak.repeat(blankLineCount + 1)}${indentation}`;
255018
+ }
255019
+ function checkChildren(children) {
255020
+ for (let i = 0; i < children.length; i++) {
255021
+ const current = children[i];
255022
+ if (!(current instanceof dist$4.TmplAstLetDeclaration)) {
255023
+ continue;
255024
+ }
255025
+ let j = i + 1;
255026
+ while (j < children.length) {
255027
+ const candidate = children[j];
255028
+ const isWhitespaceText = candidate instanceof dist$4.TmplAstText &&
255029
+ candidate.value.trim() === '';
255030
+ if (!isWhitespaceText) {
255031
+ break;
255032
+ }
255033
+ j++;
255034
+ }
255035
+ const next = children[j];
255036
+ if (!(next instanceof dist$4.TmplAstLetDeclaration) &&
255037
+ !(next instanceof dist$4.TmplAstBoundText)) {
255038
+ continue;
255039
+ }
255040
+ const currentEnd = current.sourceSpan.end.offset;
255041
+ const nextStart = next.sourceSpan.start.offset;
255042
+ const betweenText = sourceText.slice(currentEnd, nextStart);
255043
+ const hasComment = betweenText.includes('//') ||
255044
+ betweenText.includes('/*') ||
255045
+ betweenText.includes('<!--');
255046
+ if (hasComment) {
255047
+ continue;
255048
+ }
255049
+ const blankLineBetween = hasBlankLine(betweenText);
255050
+ const nextLine = next.sourceSpan.start.line;
255051
+ if (next instanceof dist$4.TmplAstLetDeclaration) {
255052
+ const currentIsSingleLine = current.sourceSpan.start.line === current.sourceSpan.end.line;
255053
+ const nextIsSingleLine = next.sourceSpan.start.line === next.sourceSpan.end.line;
255054
+ if (currentIsSingleLine && nextIsSingleLine && blankLineBetween) {
255055
+ context.report({
255056
+ fix: (fixer) => fixer.replaceTextRange([currentEnd, nextStart], buildReplacement(betweenText, nextLine, 0)),
255057
+ loc: sourceSpanToLoc(next.sourceSpan),
255058
+ messageId: 'singleLineLetSpacingUnexpectedBlankLine',
255059
+ });
255060
+ continue;
255061
+ }
255062
+ if (currentIsSingleLine &&
255063
+ !nextIsSingleLine &&
255064
+ !blankLineBetween) {
255065
+ context.report({
255066
+ fix: (fixer) => fixer.replaceTextRange([currentEnd, nextStart], buildReplacement(betweenText, nextLine, 1)),
255067
+ loc: sourceSpanToLoc(next.sourceSpan),
255068
+ messageId: 'singleLineLetSpacingMissingBlankLineBeforeMultilineLet',
255069
+ });
255070
+ continue;
255071
+ }
255072
+ if (!currentIsSingleLine && !blankLineBetween) {
255073
+ context.report({
255074
+ fix: (fixer) => fixer.replaceTextRange([currentEnd, nextStart], buildReplacement(betweenText, nextLine, 1)),
255075
+ loc: sourceSpanToLoc(next.sourceSpan),
255076
+ messageId: 'singleLineLetSpacingMissingBlankLineAfterMultilineLet',
255077
+ });
255078
+ }
255079
+ continue;
255080
+ }
255081
+ if (!blankLineBetween) {
255082
+ context.report({
255083
+ fix: (fixer) => fixer.replaceTextRange([currentEnd, nextStart], buildReplacement(betweenText, nextLine, 1)),
255084
+ loc: sourceSpanToLoc(next.sourceSpan),
255085
+ messageId: 'singleLineLetSpacingMissingBlankLineBeforeInterpolation',
255086
+ });
255087
+ }
255088
+ }
255089
+ }
255090
+ function getChildren(rawNode) {
255091
+ if (typeof rawNode === 'object' &&
255092
+ rawNode !== null &&
255093
+ 'children' in rawNode) {
255094
+ const { children } = rawNode;
255095
+ return Array.isArray(children) ? children : [];
255096
+ }
255097
+ return [];
255098
+ }
255099
+ return {
255100
+ DeferredBlock(rawNode) {
255101
+ checkChildren(getChildren(rawNode));
255102
+ },
255103
+ DeferredBlockError(rawNode) {
255104
+ checkChildren(getChildren(rawNode));
255105
+ },
255106
+ DeferredBlockLoading(rawNode) {
255107
+ checkChildren(getChildren(rawNode));
255108
+ },
255109
+ DeferredBlockPlaceholder(rawNode) {
255110
+ checkChildren(getChildren(rawNode));
255111
+ },
255112
+ Element(rawNode) {
255113
+ checkChildren(getChildren(rawNode));
255114
+ },
255115
+ ForLoopBlock(rawNode) {
255116
+ checkChildren(getChildren(rawNode));
255117
+ },
255118
+ ForLoopBlockEmpty(rawNode) {
255119
+ checkChildren(getChildren(rawNode));
255120
+ },
255121
+ IfBlockBranch(rawNode) {
255122
+ checkChildren(getChildren(rawNode));
255123
+ },
255124
+ 'Program:exit'() {
255125
+ checkChildren(getTemplateNodes(sourceCode.ast));
255126
+ },
255127
+ SwitchBlockCase(rawNode) {
255128
+ checkChildren(getChildren(rawNode));
255129
+ },
255130
+ Template(rawNode) {
255131
+ checkChildren(getChildren(rawNode));
255132
+ },
255133
+ };
255134
+ },
255135
+ meta: {
255136
+ docs: {
255137
+ description: 'Group consecutive single-line @let declarations together, while separating multiline declarations and interpolations with blank lines',
255138
+ },
255139
+ fixable: 'code',
255140
+ messages: {
255141
+ singleLineLetSpacingMissingBlankLineAfterMultilineLet: 'Multiline @let declarations should be followed by a blank line',
255142
+ singleLineLetSpacingMissingBlankLineBeforeInterpolation: '@let declarations should be separated from the following interpolation by a blank line',
255143
+ singleLineLetSpacingMissingBlankLineBeforeMultilineLet: 'Multiline @let declarations should be preceded by a blank line',
255144
+ singleLineLetSpacingUnexpectedBlankLine: 'Single-line @let declarations should not be separated by a blank line',
255145
+ },
255146
+ schema: [],
255147
+ type: 'layout',
255148
+ },
255149
+ },
255150
+ });
255151
+
254997
255152
  function getVariableSpacingStatement(node) {
254998
255153
  if (node.type === dist$3.AST_NODE_TYPES.VariableDeclaration) {
254999
255154
  return { declaration: node, exported: false, node };
@@ -256132,60 +256287,61 @@ const plugin = {
256132
256287
  },
256133
256288
  rules: {
256134
256289
  'array-as-const': rule$5,
256135
- 'attrs-newline': rule$V,
256290
+ 'attrs-newline': rule$W,
256136
256291
  'class-property-naming': rule$4,
256137
- 'decorator-key-sort': rule$U,
256138
- 'element-newline': rule$T,
256292
+ 'decorator-key-sort': rule$V,
256293
+ 'element-newline': rule$U,
256139
256294
  'flat-exports': rule$3,
256140
- 'host-attributes-sort': rule$S,
256141
- 'html-logical-properties': rule$R,
256142
- 'import-integrity': rule$Q,
256143
- 'injection-token-description': rule$P,
256144
- 'no-commonjs-import-patterns': rule$O,
256145
- 'no-deep-imports': rule$N,
256146
- 'no-deep-imports-to-indexed-packages': rule$M,
256147
- 'no-duplicate-attrs': rule$L,
256148
- 'no-duplicate-id': rule$K,
256149
- 'no-duplicate-in-head': rule$J,
256150
- 'no-empty-style-metadata': rule$I,
256151
- 'no-fully-untracked-effect': rule$H,
256152
- 'no-href-with-router-link': rule$G,
256153
- 'no-implicit-public': rule$F,
256154
- 'no-import-assertions': rule$E,
256155
- 'no-infinite-loop': rule$D,
256156
- 'no-legacy-peer-deps': rule$C,
256157
- 'no-nested-interactive': rule$B,
256158
- 'no-nested-ternary-in-template': rule$A,
256159
- 'no-obsolete-attrs': rule$z,
256160
- 'no-obsolete-tags': rule$y,
256161
- 'no-playwright-empty-fill': rule$x,
256162
- 'no-project-as-in-ng-template': rule$w,
256163
- 'no-redundant-type-annotation': rule$v,
256164
- 'no-repeated-signal-in-conditional': rule$u,
256295
+ 'host-attributes-sort': rule$T,
256296
+ 'html-logical-properties': rule$S,
256297
+ 'import-integrity': rule$R,
256298
+ 'injection-token-description': rule$Q,
256299
+ 'no-commonjs-import-patterns': rule$P,
256300
+ 'no-deep-imports': rule$O,
256301
+ 'no-deep-imports-to-indexed-packages': rule$N,
256302
+ 'no-duplicate-attrs': rule$M,
256303
+ 'no-duplicate-id': rule$L,
256304
+ 'no-duplicate-in-head': rule$K,
256305
+ 'no-empty-style-metadata': rule$J,
256306
+ 'no-fully-untracked-effect': rule$I,
256307
+ 'no-href-with-router-link': rule$H,
256308
+ 'no-implicit-public': rule$G,
256309
+ 'no-import-assertions': rule$F,
256310
+ 'no-infinite-loop': rule$E,
256311
+ 'no-legacy-peer-deps': rule$D,
256312
+ 'no-nested-interactive': rule$C,
256313
+ 'no-nested-ternary-in-template': rule$B,
256314
+ 'no-obsolete-attrs': rule$A,
256315
+ 'no-obsolete-tags': rule$z,
256316
+ 'no-playwright-empty-fill': rule$y,
256317
+ 'no-project-as-in-ng-template': rule$x,
256318
+ 'no-redundant-type-annotation': rule$w,
256319
+ 'no-repeated-signal-in-conditional': rule$v,
256165
256320
  'no-restricted-attr-values': rule$2,
256166
- 'no-side-effects-in-computed': rule$t,
256167
- 'no-signal-outside-class': rule$s,
256168
- 'no-signal-reads-after-await-in-reactive-context': rule$r,
256169
- 'no-string-literal-concat': rule$q,
256170
- 'no-untracked-outside-reactive-context': rule$p,
256171
- 'no-useless-untracked': rule$o,
256172
- 'object-single-line': rule$n,
256173
- 'prefer-combined-if-control-flow': rule$m,
256174
- 'prefer-conditional-return': rule$l,
256321
+ 'no-side-effects-in-computed': rule$u,
256322
+ 'no-signal-outside-class': rule$t,
256323
+ 'no-signal-reads-after-await-in-reactive-context': rule$s,
256324
+ 'no-string-literal-concat': rule$r,
256325
+ 'no-untracked-outside-reactive-context': rule$q,
256326
+ 'no-useless-untracked': rule$p,
256327
+ 'object-single-line': rule$o,
256328
+ 'prefer-combined-if-control-flow': rule$n,
256329
+ 'prefer-conditional-return': rule$m,
256175
256330
  'prefer-deep-imports': rule$1,
256176
- 'prefer-loose-null-check': rule$k,
256177
- 'prefer-multi-arg-push': rule$j,
256178
- 'prefer-namespace-keyword': rule$i,
256179
- 'prefer-untracked-incidental-signal-reads': rule$h,
256180
- 'prefer-untracked-signal-getter': rule$g,
256181
- quotes: rule$f,
256182
- 'require-doctype': rule$e,
256183
- 'require-img-alt': rule$d,
256184
- 'require-lang': rule$c,
256185
- 'require-li-container': rule$b,
256186
- 'require-title': rule$a,
256187
- 'short-tui-imports': rule$9,
256188
- 'single-line-class-property-spacing': rule$8,
256331
+ 'prefer-loose-null-check': rule$l,
256332
+ 'prefer-multi-arg-push': rule$k,
256333
+ 'prefer-namespace-keyword': rule$j,
256334
+ 'prefer-untracked-incidental-signal-reads': rule$i,
256335
+ 'prefer-untracked-signal-getter': rule$h,
256336
+ quotes: rule$g,
256337
+ 'require-doctype': rule$f,
256338
+ 'require-img-alt': rule$e,
256339
+ 'require-lang': rule$d,
256340
+ 'require-li-container': rule$c,
256341
+ 'require-title': rule$b,
256342
+ 'short-tui-imports': rule$a,
256343
+ 'single-line-class-property-spacing': rule$9,
256344
+ 'single-line-let-spacing': rule$8,
256189
256345
  'single-line-variable-spacing': rule$7,
256190
256346
  'standalone-imports-sort': rule$6,
256191
256347
  'strict-tui-doc-example': rule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taiga-ui/eslint-plugin-experience-next",
3
- "version": "0.519.0",
3
+ "version": "0.520.0",
4
4
  "description": "An ESLint plugin to enforce a consistent code styles across taiga-ui projects",
5
5
  "homepage": "https://github.com/taiga-family/toolkit#readme",
6
6
  "bugs": {
@@ -43,7 +43,7 @@
43
43
  "eslint-plugin-file-progress": "3.0.2",
44
44
  "eslint-plugin-import": "2.32.0",
45
45
  "eslint-plugin-jest": "29.15.2",
46
- "eslint-plugin-package-json": "0.91.1",
46
+ "eslint-plugin-package-json": "0.91.2",
47
47
  "eslint-plugin-perfectionist": "5.9.0",
48
48
  "eslint-plugin-playwright": "2.10.2",
49
49
  "eslint-plugin-prettier": "5.5.5",
@@ -0,0 +1,5 @@
1
+ import { type Rule } from 'eslint';
2
+ export declare const rule: Rule.RuleModule & {
3
+ name: string;
4
+ };
5
+ export default rule;