@taiga-ui/eslint-plugin-experience-next 0.518.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) {
@@ -250598,6 +250599,11 @@ function isInteractiveElement(node) {
250598
250599
  }
250599
250600
 
250600
250601
  const MESSAGE_ID$b = 'noNestedInteractive';
250602
+ // Elements that act as DOM portals or isolated list containers:
250603
+ // their children are rendered outside the current DOM subtree.
250604
+ const PORTAL_ELEMENTS = new Set(['tui-data-list', 'tui-textfield']);
250605
+ // Structural directives that portal their host element into an overlay.
250606
+ const PORTAL_STRUCTURAL_DIRECTIVES = new Set(['tuiDropdown']);
250601
250607
  function getAvailableLabelParent(stack, node, labelsWithControl) {
250602
250608
  const parent = stack[stack.length - 1];
250603
250609
  return stack.length === 1 &&
@@ -250607,15 +250613,33 @@ function getAvailableLabelParent(stack, node, labelsWithControl) {
250607
250613
  ? parent
250608
250614
  : null;
250609
250615
  }
250610
- const rule$B = createRule({
250616
+ const rule$C = createRule({
250611
250617
  name: 'no-nested-interactive',
250612
250618
  rule: {
250613
250619
  create(context) {
250614
250620
  const interactiveStack = [];
250615
250621
  const labelsWithControl = new WeakSet();
250622
+ const savedStackByNode = new WeakMap();
250623
+ function saveAndReset(node) {
250624
+ savedStackByNode.set(node, [...interactiveStack]);
250625
+ interactiveStack.length = 0;
250626
+ }
250627
+ function restore(node) {
250628
+ const saved = savedStackByNode.get(node);
250629
+ if (saved === undefined) {
250630
+ return;
250631
+ }
250632
+ savedStackByNode.delete(node);
250633
+ interactiveStack.length = 0;
250634
+ interactiveStack.push(...saved);
250635
+ }
250616
250636
  return {
250617
250637
  Element(rawNode) {
250618
250638
  const node = rawNode;
250639
+ if (PORTAL_ELEMENTS.has(node.name.toLowerCase())) {
250640
+ saveAndReset(node);
250641
+ return;
250642
+ }
250619
250643
  if (!isInteractiveElement(node)) {
250620
250644
  return;
250621
250645
  }
@@ -250635,10 +250659,23 @@ const rule$B = createRule({
250635
250659
  },
250636
250660
  'Element:exit'(rawNode) {
250637
250661
  const node = rawNode;
250662
+ restore(node);
250638
250663
  if (interactiveStack[interactiveStack.length - 1] === node) {
250639
250664
  interactiveStack.pop();
250640
250665
  }
250641
250666
  },
250667
+ Template(rawNode) {
250668
+ const node = rawNode;
250669
+ const isPortalBoundary = node.tagName === 'ng-template' ||
250670
+ node.templateAttrs.some((attr) => PORTAL_STRUCTURAL_DIRECTIVES.has(attr.name));
250671
+ if (isPortalBoundary) {
250672
+ saveAndReset(node);
250673
+ }
250674
+ },
250675
+ 'Template:exit'(rawNode) {
250676
+ const node = rawNode;
250677
+ restore(node);
250678
+ },
250642
250679
  };
250643
250680
  },
250644
250681
  meta: {
@@ -250845,11 +250882,12 @@ function createLetFixes(node, baseName, unavailableNames, text) {
250845
250882
  reference: name,
250846
250883
  };
250847
250884
  }
250848
- const rule$A = createRule({
250885
+ const rule$B = createRule({
250849
250886
  name: 'no-nested-ternary-in-template',
250850
250887
  rule: {
250851
250888
  create(context) {
250852
250889
  const { sourceCode } = context;
250890
+ const boundTextStack = [];
250853
250891
  const conditionalStack = [];
250854
250892
  const containerStack = [];
250855
250893
  const letNames = collectTemplateIdentifiers(sourceCode.ast);
@@ -250858,18 +250896,22 @@ const rule$A = createRule({
250858
250896
  function reportNestedConditional(node, options) {
250859
250897
  const container = containerStack[containerStack.length - 1];
250860
250898
  const attribute = getContainingBoundAttribute(container, node);
250899
+ const boundText = boundTextStack[boundTextStack.length - 1];
250861
250900
  const baseName = attribute?.name;
250862
250901
  const fixable = options.allowFix &&
250863
250902
  boundEventDepth === 0 &&
250864
250903
  letDeclarationDepth === 0 &&
250865
- baseName &&
250866
- isIdentifier(baseName);
250904
+ ((baseName !== undefined && isIdentifier(baseName)) ||
250905
+ boundText !== undefined);
250867
250906
  context.report({
250868
250907
  ...(fixable
250869
250908
  ? {
250870
250909
  fix(fixer) {
250871
- const result = createLetFixes(node, baseName, letNames, sourceCode.text);
250872
- const insertOffset = container?.startSourceSpan.start.offset;
250910
+ const effectiveName = baseName ?? 'text';
250911
+ const result = createLetFixes(node, effectiveName, letNames, sourceCode.text);
250912
+ const insertOffset = baseName === undefined
250913
+ ? boundText?.sourceSpan.start.offset
250914
+ : container?.startSourceSpan.start.offset;
250873
250915
  if (insertOffset === undefined) {
250874
250916
  return null;
250875
250917
  }
@@ -250898,6 +250940,14 @@ const rule$A = createRule({
250898
250940
  'BoundEvent:exit'() {
250899
250941
  boundEventDepth--;
250900
250942
  },
250943
+ BoundText(rawNode) {
250944
+ boundTextStack.push(rawNode);
250945
+ },
250946
+ 'BoundText:exit'(rawNode) {
250947
+ if (boundTextStack[boundTextStack.length - 1] === rawNode) {
250948
+ boundTextStack.pop();
250949
+ }
250950
+ },
250901
250951
  Conditional(rawNode) {
250902
250952
  const node = rawNode;
250903
250953
  if (conditionalStack.length > 0) {
@@ -251369,7 +251419,7 @@ const OBSOLETE_HTML_ATTRS = {
251369
251419
  };
251370
251420
 
251371
251421
  const MESSAGE_ID$9 = 'obsolete';
251372
- const rule$z = createRule({
251422
+ const rule$A = createRule({
251373
251423
  name: 'no-obsolete-attrs',
251374
251424
  rule: {
251375
251425
  create(context) {
@@ -251447,7 +251497,7 @@ const OBSOLETE_HTML_TAGS = new Set([
251447
251497
  ]);
251448
251498
 
251449
251499
  const MESSAGE_ID$8 = 'unexpected';
251450
- const rule$y = createRule({
251500
+ const rule$z = createRule({
251451
251501
  name: 'no-obsolete-tags',
251452
251502
  rule: {
251453
251503
  create(context) {
@@ -251474,7 +251524,7 @@ const rule$y = createRule({
251474
251524
  },
251475
251525
  });
251476
251526
 
251477
- const rule$x = createRule({
251527
+ const rule$y = createRule({
251478
251528
  create(context) {
251479
251529
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
251480
251530
  return {
@@ -251618,7 +251668,7 @@ const config$2 = {
251618
251668
  type: 'problem',
251619
251669
  },
251620
251670
  };
251621
- const rule$w = createRule({
251671
+ const rule$x = createRule({
251622
251672
  name: 'no-project-as-in-ng-template',
251623
251673
  rule: config$2,
251624
251674
  });
@@ -251655,7 +251705,7 @@ function collectArrayExpressions(node) {
251655
251705
  }
251656
251706
  return result;
251657
251707
  }
251658
- const rule$v = createRule({
251708
+ const rule$w = createRule({
251659
251709
  create(context) {
251660
251710
  const { checker: typeChecker, esTreeNodeToTSNodeMap } = getTypeAwareRuleContext(context);
251661
251711
  const ignoreTupleContextualTyping = context.options[0]?.ignoreTupleContextualTyping ?? true;
@@ -251853,7 +251903,7 @@ function isOptionalMemberReceiver(call) {
251853
251903
  parent.object === current &&
251854
251904
  parent.optional);
251855
251905
  }
251856
- const rule$u = createRule({
251906
+ const rule$v = createRule({
251857
251907
  create(context) {
251858
251908
  const { checker, esTreeNodeToTSNodeMap, sourceCode } = getTypeAwareRuleContext(context);
251859
251909
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252298,7 +252348,7 @@ function inspectComputedBody(root, context, localScopes, visitedFunctions, repor
252298
252348
  return;
252299
252349
  });
252300
252350
  }
252301
- const rule$t = createRule({
252351
+ const rule$u = createRule({
252302
252352
  create(context) {
252303
252353
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
252304
252354
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252379,7 +252429,7 @@ function collectModuleScopeSignalNames(program, factoryNames) {
252379
252429
  }
252380
252430
  return names;
252381
252431
  }
252382
- const rule$s = createRule({
252432
+ const rule$t = createRule({
252383
252433
  create(context) {
252384
252434
  const program = context.sourceCode.ast;
252385
252435
  const factoryNames = new Set();
@@ -252402,6 +252452,16 @@ const rule$s = createRule({
252402
252452
  !moduleScopeSignals.has(value.name)) {
252403
252453
  return;
252404
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
+ }
252405
252465
  context.report({
252406
252466
  data: { name: value.name },
252407
252467
  messageId: 'noSignalOutsideClass',
@@ -252423,7 +252483,7 @@ const rule$s = createRule({
252423
252483
  name: 'no-signal-outside-class',
252424
252484
  });
252425
252485
 
252426
- const rule$r = createUntrackedRule({
252486
+ const rule$s = createUntrackedRule({
252427
252487
  create(context) {
252428
252488
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252429
252489
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252515,7 +252575,7 @@ function templateContent(template, renderExpr) {
252515
252575
  : ''}`)
252516
252576
  .join('');
252517
252577
  }
252518
- const rule$q = createRule({
252578
+ const rule$r = createRule({
252519
252579
  create(context) {
252520
252580
  const { sourceCode } = context;
252521
252581
  let parserServices = null;
@@ -252837,7 +252897,7 @@ function buildReactiveCallReplacement(outerUntrackedCall, reactiveCall, sourceCo
252837
252897
  ? text
252838
252898
  : dedent(text, reactiveCall.loc.start.column - outerUntrackedCall.parent.loc.start.column);
252839
252899
  }
252840
- const rule$p = createUntrackedRule({
252900
+ const rule$q = createUntrackedRule({
252841
252901
  create(context) {
252842
252902
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252843
252903
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -252966,7 +253026,7 @@ function hasOpaqueSynchronousCalls(root, checker, esTreeNodeToTSNodeMap, program
252966
253026
  });
252967
253027
  return found;
252968
253028
  }
252969
- const rule$o = createUntrackedRule({
253029
+ const rule$p = createUntrackedRule({
252970
253030
  create(context) {
252971
253031
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
252972
253032
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -253048,7 +253108,7 @@ const rule$o = createUntrackedRule({
253048
253108
  name: 'no-useless-untracked',
253049
253109
  });
253050
253110
 
253051
- const rule$n = createRule({
253111
+ const rule$o = createRule({
253052
253112
  create(context, [{ printWidth }]) {
253053
253113
  const sourceCode = context.sourceCode;
253054
253114
  const getLineEndIndex = (lineStartIndex) => {
@@ -253362,7 +253422,7 @@ function renderTest(node, sourceCode) {
253362
253422
  const text = sourceCode.getText(node);
253363
253423
  return needsParenthesesInOrChain(node) ? `(${text})` : text;
253364
253424
  }
253365
- const rule$m = createRule({
253425
+ const rule$n = createRule({
253366
253426
  create(context) {
253367
253427
  const { sourceCode } = context;
253368
253428
  function checkBody(statements) {
@@ -253659,7 +253719,7 @@ function renderBooleanTestReturn(ifStatement, sourceCode, strategy) {
253659
253719
  const indent = getStatementIndent(ifStatement, sourceCode);
253660
253720
  return `return (\n${indent} ${test}\n${indent});`;
253661
253721
  }
253662
- const rule$l = createRule({
253722
+ const rule$m = createRule({
253663
253723
  create(context) {
253664
253724
  const { sourceCode } = context;
253665
253725
  let parserServices = null;
@@ -253802,7 +253862,7 @@ function findLooseNullCheckMatch(parsedChecks) {
253802
253862
  }
253803
253863
  return null;
253804
253864
  }
253805
- const rule$k = createRule({
253865
+ const rule$l = createRule({
253806
253866
  create(context) {
253807
253867
  const getText = (n) => context.sourceCode.getText(n);
253808
253868
  return {
@@ -253860,7 +253920,7 @@ function getPushCall(node) {
253860
253920
  ? null
253861
253921
  : call;
253862
253922
  }
253863
- const rule$j = createRule({
253923
+ const rule$k = createRule({
253864
253924
  create(context) {
253865
253925
  const { sourceCode } = context;
253866
253926
  function checkBody(statements) {
@@ -253955,7 +254015,7 @@ function getModuleKeywordToken(sourceCode, node) {
253955
254015
  ? (sourceCode.getTokenAfter(firstToken) ?? null)
253956
254016
  : firstToken;
253957
254017
  }
253958
- const rule$i = createRule({
254018
+ const rule$j = createRule({
253959
254019
  create(context) {
253960
254020
  const { sourceCode } = context;
253961
254021
  return {
@@ -254222,7 +254282,7 @@ function collectSuspiciousReads(scope, checker, esTreeNodeToTSNodeMap, tsNodeToE
254222
254282
  });
254223
254283
  return [...suspicious.values()];
254224
254284
  }
254225
- const rule$h = createUntrackedRule({
254285
+ const rule$i = createUntrackedRule({
254226
254286
  create(context) {
254227
254287
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode, tsNodeToESTreeNodeMap, } = getTypeAwareRuleContext(context);
254228
254288
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -254308,7 +254368,7 @@ function getWrappedSignalGetter(node, checker, esTreeNodeToTSNodeMap) {
254308
254368
  }
254309
254369
  return isSignalType(getter, checker, esTreeNodeToTSNodeMap) ? body.callee : null;
254310
254370
  }
254311
- const rule$g = createUntrackedRule({
254371
+ const rule$h = createUntrackedRule({
254312
254372
  create(context) {
254313
254373
  const { checker, esTreeNodeToTSNodeMap, program, sourceCode } = getTypeAwareRuleContext(context);
254314
254374
  const signalNodeMap = esTreeNodeToTSNodeMap;
@@ -254354,7 +254414,7 @@ const DOUBLE_QUOTE = '"';
254354
254414
  function isQuotableAttribute(attr) {
254355
254415
  return 'sourceSpan' in attr;
254356
254416
  }
254357
- const rule$f = createRule({
254417
+ const rule$g = createRule({
254358
254418
  name: 'quotes',
254359
254419
  rule: {
254360
254420
  create(context) {
@@ -254439,7 +254499,7 @@ const rule$f = createRule({
254439
254499
 
254440
254500
  const MESSAGE_ID$6 = 'missing';
254441
254501
  const DOCTYPE_REGEXP = /^\s*<!doctype html>/i;
254442
- const rule$e = createRule({
254502
+ const rule$f = createRule({
254443
254503
  name: 'require-doctype',
254444
254504
  rule: {
254445
254505
  create(context) {
@@ -254473,7 +254533,7 @@ const rule$e = createRule({
254473
254533
  });
254474
254534
 
254475
254535
  const MESSAGE_ID$5 = 'missingAlt';
254476
- const rule$d = createRule({
254536
+ const rule$e = createRule({
254477
254537
  name: 'require-img-alt',
254478
254538
  rule: {
254479
254539
  create(context) {
@@ -254503,7 +254563,7 @@ const MESSAGE_IDS$1 = {
254503
254563
  EMPTY: 'empty',
254504
254564
  MISSING: 'missing',
254505
254565
  };
254506
- const rule$c = createRule({
254566
+ const rule$d = createRule({
254507
254567
  name: 'require-lang',
254508
254568
  rule: {
254509
254569
  create(context) {
@@ -254565,7 +254625,7 @@ function getClosestParentElement(node) {
254565
254625
  }
254566
254626
  return null;
254567
254627
  }
254568
- const rule$b = createRule({
254628
+ const rule$c = createRule({
254569
254629
  name: 'require-li-container',
254570
254630
  rule: {
254571
254631
  create(context) {
@@ -254613,7 +254673,7 @@ function hasMeaningfulTitleContent(node) {
254613
254673
  (typeof value === 'string' && value.trim().length > 0));
254614
254674
  });
254615
254675
  }
254616
- const rule$a = createRule({
254676
+ const rule$b = createRule({
254617
254677
  name: 'require-title',
254618
254678
  rule: {
254619
254679
  create(context) {
@@ -254702,7 +254762,7 @@ const DEFAULT_EXCEPTIONS = [
254702
254762
  { from: 'TuiIslandDirective', to: 'TuiIsland' },
254703
254763
  { from: 'TuiTableBarsHostComponent', to: 'TuiTableBarsHost' },
254704
254764
  ];
254705
- const rule$9 = createRule({
254765
+ const rule$a = createRule({
254706
254766
  create(context, [{ decorators = DEFAULT_DECORATORS, exceptions = DEFAULT_EXCEPTIONS }]) {
254707
254767
  const sourceCode = context.getSourceCode();
254708
254768
  const allowedDecorators = new Set(decorators);
@@ -254861,7 +254921,7 @@ function isRelevantSpacingClassMember(member) {
254861
254921
  return isFieldLikeMember(member) || isAccessorMember(member);
254862
254922
  }
254863
254923
 
254864
- const rule$8 = createRule({
254924
+ const rule$9 = createRule({
254865
254925
  create(context) {
254866
254926
  const sourceCode = context.sourceCode;
254867
254927
  return {
@@ -254945,6 +255005,150 @@ const rule$8 = createRule({
254945
255005
  name: 'single-line-class-property-spacing',
254946
255006
  });
254947
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
+
254948
255152
  function getVariableSpacingStatement(node) {
254949
255153
  if (node.type === dist$3.AST_NODE_TYPES.VariableDeclaration) {
254950
255154
  return { declaration: node, exported: false, node };
@@ -256083,60 +256287,61 @@ const plugin = {
256083
256287
  },
256084
256288
  rules: {
256085
256289
  'array-as-const': rule$5,
256086
- 'attrs-newline': rule$V,
256290
+ 'attrs-newline': rule$W,
256087
256291
  'class-property-naming': rule$4,
256088
- 'decorator-key-sort': rule$U,
256089
- 'element-newline': rule$T,
256292
+ 'decorator-key-sort': rule$V,
256293
+ 'element-newline': rule$U,
256090
256294
  'flat-exports': rule$3,
256091
- 'host-attributes-sort': rule$S,
256092
- 'html-logical-properties': rule$R,
256093
- 'import-integrity': rule$Q,
256094
- 'injection-token-description': rule$P,
256095
- 'no-commonjs-import-patterns': rule$O,
256096
- 'no-deep-imports': rule$N,
256097
- 'no-deep-imports-to-indexed-packages': rule$M,
256098
- 'no-duplicate-attrs': rule$L,
256099
- 'no-duplicate-id': rule$K,
256100
- 'no-duplicate-in-head': rule$J,
256101
- 'no-empty-style-metadata': rule$I,
256102
- 'no-fully-untracked-effect': rule$H,
256103
- 'no-href-with-router-link': rule$G,
256104
- 'no-implicit-public': rule$F,
256105
- 'no-import-assertions': rule$E,
256106
- 'no-infinite-loop': rule$D,
256107
- 'no-legacy-peer-deps': rule$C,
256108
- 'no-nested-interactive': rule$B,
256109
- 'no-nested-ternary-in-template': rule$A,
256110
- 'no-obsolete-attrs': rule$z,
256111
- 'no-obsolete-tags': rule$y,
256112
- 'no-playwright-empty-fill': rule$x,
256113
- 'no-project-as-in-ng-template': rule$w,
256114
- 'no-redundant-type-annotation': rule$v,
256115
- '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,
256116
256320
  'no-restricted-attr-values': rule$2,
256117
- 'no-side-effects-in-computed': rule$t,
256118
- 'no-signal-outside-class': rule$s,
256119
- 'no-signal-reads-after-await-in-reactive-context': rule$r,
256120
- 'no-string-literal-concat': rule$q,
256121
- 'no-untracked-outside-reactive-context': rule$p,
256122
- 'no-useless-untracked': rule$o,
256123
- 'object-single-line': rule$n,
256124
- 'prefer-combined-if-control-flow': rule$m,
256125
- '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,
256126
256330
  'prefer-deep-imports': rule$1,
256127
- 'prefer-loose-null-check': rule$k,
256128
- 'prefer-multi-arg-push': rule$j,
256129
- 'prefer-namespace-keyword': rule$i,
256130
- 'prefer-untracked-incidental-signal-reads': rule$h,
256131
- 'prefer-untracked-signal-getter': rule$g,
256132
- quotes: rule$f,
256133
- 'require-doctype': rule$e,
256134
- 'require-img-alt': rule$d,
256135
- 'require-lang': rule$c,
256136
- 'require-li-container': rule$b,
256137
- 'require-title': rule$a,
256138
- 'short-tui-imports': rule$9,
256139
- '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,
256140
256345
  'single-line-variable-spacing': rule$7,
256141
256346
  'standalone-imports-sort': rule$6,
256142
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.518.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;