lint-suite 1.6.3 → 2.0.1

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.
Files changed (183) hide show
  1. package/README.md +117 -20
  2. package/eslint.cjs +3312 -492
  3. package/eslint.cjs.map +4 -4
  4. package/eslint.js +3304 -445
  5. package/eslint.js.map +4 -4
  6. package/lib/angular-template.d.ts +0 -1
  7. package/lib/angular-template.d.ts.map +1 -1
  8. package/lib/angular.d.ts +0 -1
  9. package/lib/angular.d.ts.map +1 -1
  10. package/lib/base-limits.d.ts.map +1 -1
  11. package/lib/rules/arrow-body-fits-line/arrow-body-fits-line.d.ts +6 -0
  12. package/lib/rules/arrow-body-fits-line/arrow-body-fits-line.d.ts.map +1 -0
  13. package/lib/rules/chain-fits-line/chain-fits-line.d.ts +6 -0
  14. package/lib/rules/chain-fits-line/chain-fits-line.d.ts.map +1 -0
  15. package/lib/rules/chain-receiver-is-name/chain-receiver-is-name.d.ts +7 -0
  16. package/lib/rules/chain-receiver-is-name/chain-receiver-is-name.d.ts.map +1 -0
  17. package/lib/rules/common/class-usage.type.d.ts +1 -0
  18. package/lib/rules/common/class-usage.type.d.ts.map +1 -1
  19. package/lib/rules/common/file-cache.type.d.ts +11 -0
  20. package/lib/rules/common/file-cache.type.d.ts.map +1 -0
  21. package/lib/rules/common/glob-matcher.type.d.ts +2 -0
  22. package/lib/rules/common/glob-matcher.type.d.ts.map +1 -0
  23. package/lib/rules/component-metadata.d.ts +2 -4
  24. package/lib/rules/component-metadata.d.ts.map +1 -1
  25. package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts +1 -1
  26. package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts.map +1 -1
  27. package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts +4 -1
  28. package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts.map +1 -1
  29. package/lib/rules/file-cache.d.ts +14 -0
  30. package/lib/rules/file-cache.d.ts.map +1 -0
  31. package/lib/rules/max-condition-operands/max-condition-operands.d.ts +10 -0
  32. package/lib/rules/max-condition-operands/max-condition-operands.d.ts.map +1 -0
  33. package/lib/rules/no-call-in-condition/common/no-call-in-condition.const.d.ts +6 -0
  34. package/lib/rules/no-call-in-condition/common/no-call-in-condition.const.d.ts.map +1 -0
  35. package/lib/rules/no-call-in-condition/common/no-call-in-condition.type.d.ts +21 -0
  36. package/lib/rules/no-call-in-condition/common/no-call-in-condition.type.d.ts.map +1 -0
  37. package/lib/rules/no-call-in-condition/no-call-in-condition.d.ts +7 -0
  38. package/lib/rules/no-call-in-condition/no-call-in-condition.d.ts.map +1 -0
  39. package/lib/rules/no-call-in-condition/utils/call-exemptions.util.d.ts +4 -0
  40. package/lib/rules/no-call-in-condition/utils/call-exemptions.util.d.ts.map +1 -0
  41. package/lib/rules/no-call-in-condition/utils/condition-calls.util.d.ts +7 -0
  42. package/lib/rules/no-call-in-condition/utils/condition-calls.util.d.ts.map +1 -0
  43. package/lib/rules/no-grouped-condition/no-grouped-condition.d.ts +7 -0
  44. package/lib/rules/no-grouped-condition/no-grouped-condition.d.ts.map +1 -0
  45. package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts.map +1 -1
  46. package/lib/rules/no-inline-return-object/no-inline-return-object.d.ts +6 -0
  47. package/lib/rules/no-inline-return-object/no-inline-return-object.d.ts.map +1 -0
  48. package/lib/rules/no-nested-object-value/common/no-nested-object-value.const.d.ts +5 -0
  49. package/lib/rules/no-nested-object-value/common/no-nested-object-value.const.d.ts.map +1 -0
  50. package/lib/rules/no-nested-object-value/common/no-nested-object-value.type.d.ts +7 -0
  51. package/lib/rules/no-nested-object-value/common/no-nested-object-value.type.d.ts.map +1 -0
  52. package/lib/rules/no-nested-object-value/no-nested-object-value.d.ts +7 -0
  53. package/lib/rules/no-nested-object-value/no-nested-object-value.d.ts.map +1 -0
  54. package/lib/rules/no-nested-object-value/utils/decorator-config.util.d.ts +3 -0
  55. package/lib/rules/no-nested-object-value/utils/decorator-config.util.d.ts.map +1 -0
  56. package/lib/rules/no-spread-expression/no-spread-expression.d.ts +6 -0
  57. package/lib/rules/no-spread-expression/no-spread-expression.d.ts.map +1 -0
  58. package/lib/rules/no-unstyled-classes/no-unstyled-classes.d.ts.map +1 -1
  59. package/lib/rules/no-unstyled-classes/styles/component-stylesheets.d.ts.map +1 -1
  60. package/lib/rules/no-unstyled-classes/styles/linked-stylesheets.d.ts +3 -0
  61. package/lib/rules/no-unstyled-classes/styles/linked-stylesheets.d.ts.map +1 -0
  62. package/lib/rules/no-unstyled-classes/styles/stylesheet-classes.d.ts.map +1 -1
  63. package/lib/rules/no-unstyled-classes/styles/template-stylesheets.d.ts +1 -1
  64. package/lib/rules/no-unstyled-classes/styles/template-stylesheets.d.ts.map +1 -1
  65. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts +5 -0
  66. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts.map +1 -1
  67. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.d.ts.map +1 -1
  68. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts +0 -1
  69. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts.map +1 -1
  70. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts.map +1 -1
  71. package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.d.ts.map +1 -1
  72. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts +6 -8
  73. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts.map +1 -1
  74. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts +3 -14
  75. package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts.map +1 -1
  76. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.d.ts.map +1 -1
  77. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts +3 -2
  78. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts.map +1 -1
  79. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts +1 -1
  80. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts.map +1 -1
  81. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts +8 -4
  82. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts.map +1 -1
  83. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts +3 -2
  84. package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts.map +1 -1
  85. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts.map +1 -1
  86. package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts.map +1 -1
  87. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/class-members.util.d.ts +10 -0
  88. package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/class-members.util.d.ts.map +1 -0
  89. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts.map +1 -1
  90. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.d.ts.map +1 -1
  91. package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.d.ts.map +1 -1
  92. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts +2 -1
  93. package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts.map +1 -1
  94. package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts.map +1 -1
  95. package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.d.ts.map +1 -1
  96. package/lib/rules/no-unused-classes/no-unused-classes.d.ts.map +1 -1
  97. package/lib/rules/no-unused-classes/styles/linked-templates.d.ts +9 -0
  98. package/lib/rules/no-unused-classes/styles/linked-templates.d.ts.map +1 -0
  99. package/lib/rules/no-unused-classes/styles/stylesheet-components.d.ts.map +1 -1
  100. package/lib/rules/no-unused-classes/template/template-usage.d.ts.map +1 -1
  101. package/lib/rules/no-unused-exports/common/no-unused-exports.const.d.ts +7 -0
  102. package/lib/rules/no-unused-exports/common/no-unused-exports.const.d.ts.map +1 -0
  103. package/lib/rules/no-unused-exports/common/no-unused-exports.type.d.ts +70 -0
  104. package/lib/rules/no-unused-exports/common/no-unused-exports.type.d.ts.map +1 -0
  105. package/lib/rules/no-unused-exports/export-index.d.ts +6 -0
  106. package/lib/rules/no-unused-exports/export-index.d.ts.map +1 -0
  107. package/lib/rules/no-unused-exports/no-unused-exports.d.ts +7 -0
  108. package/lib/rules/no-unused-exports/no-unused-exports.d.ts.map +1 -0
  109. package/lib/rules/no-unused-exports/report-unused-exports.d.ts +12 -0
  110. package/lib/rules/no-unused-exports/report-unused-exports.d.ts.map +1 -0
  111. package/lib/rules/no-unused-exports/utils/aggregate-update.util.d.ts +4 -0
  112. package/lib/rules/no-unused-exports/utils/aggregate-update.util.d.ts.map +1 -0
  113. package/lib/rules/no-unused-exports/utils/export-edges.util.d.ts +5 -0
  114. package/lib/rules/no-unused-exports/utils/export-edges.util.d.ts.map +1 -0
  115. package/lib/rules/no-unused-exports/utils/export-nodes.util.d.ts +5 -0
  116. package/lib/rules/no-unused-exports/utils/export-nodes.util.d.ts.map +1 -0
  117. package/lib/rules/no-unused-exports/utils/export-usage.util.d.ts +4 -0
  118. package/lib/rules/no-unused-exports/utils/export-usage.util.d.ts.map +1 -0
  119. package/lib/rules/no-unused-exports/utils/exported-names.util.d.ts +3 -0
  120. package/lib/rules/no-unused-exports/utils/exported-names.util.d.ts.map +1 -0
  121. package/lib/rules/no-unused-exports/utils/file-changes.util.d.ts +4 -0
  122. package/lib/rules/no-unused-exports/utils/file-changes.util.d.ts.map +1 -0
  123. package/lib/rules/no-unused-exports/utils/module-edges.util.d.ts +4 -0
  124. package/lib/rules/no-unused-exports/utils/module-edges.util.d.ts.map +1 -0
  125. package/lib/rules/no-unused-exports/utils/module-target.util.d.ts +4 -0
  126. package/lib/rules/no-unused-exports/utils/module-target.util.d.ts.map +1 -0
  127. package/lib/rules/no-unused-exports/utils/public-exports.util.d.ts +3 -0
  128. package/lib/rules/no-unused-exports/utils/public-exports.util.d.ts.map +1 -0
  129. package/lib/rules/one-line-guard/one-line-guard.d.ts.map +1 -1
  130. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts +2 -1
  131. package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts.map +1 -1
  132. package/lib/rules/{utils/selector-classes.util.d.ts → selector-classes.d.ts} +2 -2
  133. package/lib/rules/selector-classes.d.ts.map +1 -0
  134. package/lib/rules/ternary-branch-shape/ternary-branch-shape.d.ts +6 -0
  135. package/lib/rules/ternary-branch-shape/ternary-branch-shape.d.ts.map +1 -0
  136. package/lib/rules/test-file-shape/common/test-file-shape.const.d.ts +14 -0
  137. package/lib/rules/test-file-shape/common/test-file-shape.const.d.ts.map +1 -0
  138. package/lib/rules/test-file-shape/common/test-file-shape.type.d.ts +7 -0
  139. package/lib/rules/test-file-shape/common/test-file-shape.type.d.ts.map +1 -0
  140. package/lib/rules/test-file-shape/mock-exports.d.ts +7 -0
  141. package/lib/rules/test-file-shape/mock-exports.d.ts.map +1 -0
  142. package/lib/rules/test-file-shape/stub-exports.d.ts +7 -0
  143. package/lib/rules/test-file-shape/stub-exports.d.ts.map +1 -0
  144. package/lib/rules/test-file-shape/test-file-shape.d.ts +7 -0
  145. package/lib/rules/test-file-shape/test-file-shape.d.ts.map +1 -0
  146. package/lib/rules/type-placement/common/type-placement.const.d.ts +5 -0
  147. package/lib/rules/type-placement/common/type-placement.const.d.ts.map +1 -0
  148. package/lib/rules/type-placement/common/type-placement.type.d.ts +7 -0
  149. package/lib/rules/type-placement/common/type-placement.type.d.ts.map +1 -0
  150. package/lib/rules/type-placement/type-placement.d.ts +7 -0
  151. package/lib/rules/type-placement/type-placement.d.ts.map +1 -0
  152. package/lib/rules/util-purity/common/util-purity.const.d.ts +10 -0
  153. package/lib/rules/util-purity/common/util-purity.const.d.ts.map +1 -0
  154. package/lib/rules/util-purity/common/util-purity.type.d.ts +7 -0
  155. package/lib/rules/util-purity/common/util-purity.type.d.ts.map +1 -0
  156. package/lib/rules/util-purity/util-purity.d.ts +7 -0
  157. package/lib/rules/util-purity/util-purity.d.ts.map +1 -0
  158. package/lib/rules/utils/class-expression-literals.util.d.ts.map +1 -1
  159. package/lib/rules/utils/enclosing-statement.util.d.ts +3 -0
  160. package/lib/rules/utils/enclosing-statement.util.d.ts.map +1 -0
  161. package/lib/rules/utils/glob-matcher.util.d.ts +3 -0
  162. package/lib/rules/utils/glob-matcher.util.d.ts.map +1 -0
  163. package/lib/rules/utils/indent-of.util.d.ts +3 -0
  164. package/lib/rules/utils/indent-of.util.d.ts.map +1 -0
  165. package/lib/rules/utils/template-classes.util.d.ts.map +1 -1
  166. package/lib/rules/utils/template-links.util.d.ts +2 -0
  167. package/lib/rules/utils/template-links.util.d.ts.map +1 -0
  168. package/lib/rules/utils/to-regexp.util.d.ts +2 -0
  169. package/lib/rules/utils/to-regexp.util.d.ts.map +1 -0
  170. package/lib/typescript-local-plugin.d.ts.map +1 -1
  171. package/lib/typescript-safety.d.ts.map +1 -1
  172. package/lib/typescript.d.ts +0 -1
  173. package/lib/typescript.d.ts.map +1 -1
  174. package/lib/vitest.d.ts.map +1 -1
  175. package/package.json +1 -1
  176. package/stylelint.cjs +303 -99
  177. package/stylelint.cjs.map +4 -4
  178. package/stylelint.d.ts.map +1 -1
  179. package/stylelint.js +321 -111
  180. package/stylelint.js.map +4 -4
  181. package/lib/rules/no-unused-classes/styles/component-descriptors.d.ts +0 -3
  182. package/lib/rules/no-unused-classes/styles/component-descriptors.d.ts.map +0 -1
  183. package/lib/rules/utils/selector-classes.util.d.ts.map +0 -1
package/README.md CHANGED
@@ -106,6 +106,18 @@ export default [
106
106
  | changes, do not use ESLint `--cache` for correctness gates; run a full non-cached lint (for example, |
107
107
  | `eslint --no-cache`). |
108
108
 
109
+ ## Disk cache
110
+
111
+ `no-unstyled-classes`, `no-unused-classes`, and `no-unused-instance-fields`
112
+ parse component metadata, stylesheets, and templates once per file and keep
113
+ the result in memory for the life of the process, keyed by the file's mtime
114
+ and size. The same entries are mirrored to
115
+ `node_modules/.cache/lint-suite/*.json` under the current working directory
116
+ at process exit, so the next ESLint or stylelint process (a CI run, or an
117
+ editor's first lint) skips every parse of a file that did not change. Set
118
+ `LINT_SUITE_CACHE_DIR` to move the directory, or `LINT_SUITE_CACHE=0` to
119
+ keep everything in memory only.
120
+
109
121
  ## Customization
110
122
 
111
123
  You can override any rules by adding a `rules` section to your ESLint config:
@@ -152,6 +164,13 @@ templates can read a component or directive member:
152
164
  }
153
165
  ```
154
166
 
167
+ - In an editor session the index is updated incrementally: a save re-indexes
168
+ only the saved file and the files whose resolutions depended on it. The
169
+ saved file's own templates, and the templates of every component in its
170
+ folder, are re-read on every lint; edits to templates in other folders are
171
+ picked up on a throttled schedule (at most every 100× the duration of the
172
+ last check), so a cross-folder template edit can take a moment to show up
173
+ in another file's diagnostics.
155
174
  - `analysis` defaults to `'local'`. Project mode excludes spec-file reads. A
156
175
  file it cannot index exactly (a template that does not parse, metadata it
157
176
  cannot evaluate, a read it cannot type) falls back to name matching for
@@ -193,7 +212,11 @@ reported. `[ngClass]` is deliberately not analysed.
193
212
  Stylesheets come from the component beside the template: `styleUrl`,
194
213
  `styleUrls`, and inline `styles` read as string or template literals from the
195
214
  `@Component` metadata, falling back to a sibling `.scss` or `.css` file when
196
- the metadata declares none. Each stylesheet is parsed with `postcss-scss`, so
215
+ the metadata declares none. A `<link rel="stylesheet" href="...">` in the
216
+ template itself is read too, resolved against the template's directory, so a
217
+ plain HTML page without a component is judged against the stylesheets it
218
+ links; root-relative (`/x.css`) and absolute (`https://...`) links are
219
+ skipped. Each stylesheet is parsed with `postcss-scss`, so
197
220
  `&__element`, `&--modifier`, `&.other`, `& > .child`, `.wrapper &`, and rules
198
221
  nested inside `@media` all resolve against their parent selector, and a
199
222
  selector list such as `.a, .b { &__x {} }` yields both `.a__x` and `.b__x`.
@@ -243,9 +266,13 @@ file; each matching component contributes its `templateUrl` file or its inline
243
266
  `template` literal, and their classes are merged, so a stylesheet shared by two
244
267
  components is judged against both templates. When no component declares the
245
268
  stylesheet, a sibling template of the same name (`card.component.scss` →
246
- `card.component.html`) is used instead. A partial (`_tokens.scss`) or a global
247
- `styles.scss` that no component declares has no template, and the rule stays
248
- silent.
269
+ `card.component.html`) and every `.html` file in the same directory whose
270
+ `<link rel="stylesheet" href="...">` resolves to it are used instead, plus
271
+ every template anywhere under the working directory whose link resolves to
272
+ it (`node_modules`, `dist`, `coverage`, and dot-folders are skipped), so a
273
+ plain `styles.css` beside an `index.html` and a shared stylesheet linked from
274
+ other folders are both checked. A partial (`_tokens.scss`) or a global
275
+ `styles.scss` that nothing links has no template, and the rule stays silent.
249
276
 
250
277
  Selectors resolve through the same parser as the ESLint rule, so `&__element`,
251
278
  `&--modifier`, `&.other`, `& > .child`, `.wrapper &`, `@media` blocks, and
@@ -339,6 +366,9 @@ type User = { readonly name: string; roles: string[]; profile: Profile };
339
366
  - Arrays, object types, type references (including string-union aliases
340
367
  like `Status`), functions, and tuples are left untouched because the
341
368
  rule is syntactic and does not resolve types.
369
+ - The same rule reports and fixes `readonly T[]` and `ReadonlyArray<T>` to
370
+ `T[]`: the property reference is readonly, the array contents stay
371
+ mutable.
342
372
  - Index signatures, mapped types, and method signatures are out of scope.
343
373
  - Use `// eslint-disable-next-line local/readonly-type-properties` when a
344
374
  property genuinely needs to stay mutable.
@@ -346,29 +376,25 @@ type User = { readonly name: string; roles: string[]; profile: Profile };
346
376
  ### No inline object types
347
377
 
348
378
  The `typescript` preset enables `local/no-inline-object-types`, which
349
- reports object type literals nested inside a `type NAME = ...` alias
350
- declaration (nested properties, array element types, union members,
351
- intersection members, and generic arguments such as `Readonly<{...}>`).
352
- It is not auto-fixable: extracting an inline object type requires
353
- choosing a name.
379
+ reports every object type literal that is not the direct body of a
380
+ `type NAME = ...` alias: nested properties, array element types, union and
381
+ intersection members, generic arguments such as `Readonly<{...}>`, function
382
+ parameter and return types, `satisfies` targets, interface and class
383
+ members, and members of a `declare module` block. It is not auto-fixable:
384
+ extracting an inline object type requires choosing a name.
354
385
 
355
386
  ```ts
356
387
  // Before
357
- type LineItem = { readonly name: string; readonly item: { readonly id: string } };
388
+ const describe = (field: { readonly name: string }): { readonly label: string } => ...
358
389
 
359
390
  // After
360
- type Item = { readonly id: string };
361
- type LineItem = { readonly name: string; readonly item: Item };
391
+ type Field = { readonly name: string };
392
+ type FieldSummary = { readonly label: string };
393
+ const describe = (field: Field): FieldSummary => ...
362
394
  ```
363
395
 
364
- - The direct body of a `type X = {...}` alias is allowed to be an object
365
- literal; any object type literal nested inside that alias must reference
366
- a named type instead.
367
- - Positions outside a type alias — function parameters and return types,
368
- `as`/`satisfies` expressions, generic call arguments, interface members,
369
- and class members — are not checked by this rule.
370
- - Declare the shape as `type Item = {...}` and reference it instead of
371
- inlining the object type.
396
+ - The direct body of a `type X = {...}` alias is the only allowed position.
397
+ - Mapped types (`{ [K in Keys]: T }`) are a different node and stay valid.
372
398
 
373
399
  ### One-line guard
374
400
 
@@ -402,6 +428,77 @@ if (!user) return null;
402
428
  single-line guard once it exists, while `local/one-line-guard` is what
403
429
  collapses a braced guard down to one line in the first place.
404
430
 
431
+ ### Statement shape rules
432
+
433
+ The `typescript` preset enables a family of small syntactic rules that make
434
+ control flow and data shape visible by reading the code's outline. None of
435
+ them reads type information or the filesystem; each listens to one node
436
+ type and reports in microseconds per file. Where a fix needs a name the rule
437
+ offers an IDE suggestion with a placeholder name instead of an auto-fix.
438
+
439
+ | Rule | Reports | Fix |
440
+ |---|---|---|
441
+ | `local/no-call-in-condition` | A function call inside an `if` condition, or inside a boolean `const` that an `if` tests. Zero-argument `this.x()` calls (Angular signal reads) and type-predicate calls are exempt; predicates are found through scope in the same file, or through the type checker when a program is available. Option `allowPredicates` (regex sources, default `['^(is\|has)[A-Z]']`) applies when there is no program. | Suggestion: hoist to a `const` |
442
+ | `local/max-condition-operands` | An `if` condition with more than `max` (default 3) operands joined by `&&` / `\|\|`. | none |
443
+ | `local/no-grouped-condition` | A parenthesised group with a different operator inside an `if` condition or a boolean `const` (`a && (b \|\| c)`). | Suggestion: hoist the group |
444
+ | `local/ternary-branch-shape` | A ternary branch that is not a name, literal, template literal, or plain member access. | none |
445
+ | `local/chain-receiver-is-name` | A member chain starting on an inline expression: `(a ?? b).x`, `{...}.x`, `[...].x`, `(await p).x`. | Suggestion: name the receiver |
446
+ | `local/chain-fits-line` | A chain of two or more calls whose `.method(` parts sit on different lines. A multi-line callback argument does not count. | none |
447
+ | `local/arrow-body-fits-line` | An expression-bodied arrow whose body wraps onto more lines. | Fix: block body with `return` |
448
+ | `local/no-nested-object-value` | A property value that is a non-empty object literal, an array holding object literals, a ternary, or a call chain. Decorator arguments (`@Component({...})`) and files matching `configFiles` (default `**/*.config.*`, `**/eslint.config.*`, `**/*.schema.ts`) are exempt. | Suggestion: hoist to a `const` |
449
+ | `local/no-spread-expression` | `...(expr)` where the argument is not a name or member access. | Suggestion: hoist to a `const` |
450
+ | `local/no-inline-return-object` | `return {...}` and `=> ({...})`. | Suggestion: `const result = {...}; return result;` |
451
+
452
+ ### Project layout rules
453
+
454
+ Also in the `typescript` preset. These read only the file's own path and
455
+ return no listeners for files they do not cover, so they cost one regex
456
+ per file.
457
+
458
+ | Rule | Reports | Options |
459
+ |---|---|---|
460
+ | `local/type-placement` | An exported `type` outside a `common/*.type.ts` or `test/common/*.type.ts` file; a value exported from a `*.type.ts` file; a non-`const` export from a `*.const.ts` file; an `import type` from a relative or internal path that is neither a `*.type.ts` / `*.schema.ts` file nor a `common` barrel (`../common`, `./common/index.ts`, `@shared/common`). `.spec.ts`, `.stub.ts`, `.schema.ts` (an inferred type lives beside its schema), `.d.ts`, `state.type.ts` (any prefix), and fixtures are exempt; a `.spec.util.ts` file is not, so an exported type in one is reported. Never resolves imports. | `internalPatterns`: regex sources for alias prefixes that must resolve to a `*.type.ts` file. Default empty: a workspace alias (`@shared/common`) resolves to a library entry point, and module boundaries forbid deep imports, so alias type imports pass. |
461
+ | `local/util-purity` | Inside `utils/*.util.ts` (excluding paths under `test/` or `testing/`): imports of `node:fs`, `child_process`, `os`, `process`, `http`, `net`, `worker_threads`; a module-level `let`; a module-level `new Map/Set/WeakMap/WeakSet`; `process.*`, `globalThis`, `window`, `document`, `localStorage`, `console`; `Date.now`, `Math.random`, `performance.now`, `crypto.randomUUID`; `setTimeout`, `setInterval`, `fetch`, `inject`, `require`. | `bannedModules` |
462
+ | `local/test-file-shape` | A file named `*.spec-support.ts`, `*.spec-helper.ts`, `*.test-utils.ts`, `*-fixture.ts`, or under `__mocks__/` / `helpers/` / `common/stubs/`; a `.stub.ts` outside `test/stubs/`, a `.mock.ts` outside `test/mocks/`, a `.spec.util.ts` outside `test/utils/`, or any `fixtures/` directory outside `test/fixtures/` (a dedicated `testing/` library is exempt from all of these); a `test/stubs/*.stub.ts` export not named `UPPER_SNAKE_STUB` or without a type annotation; a `test/mocks/*.mock.ts` export that is not a camelCase `...Mock` function with an explicit return type. | none |
463
+
464
+ ### No unused exports
465
+
466
+ The `typescript` preset enables `local/no-unused-exports`, which reports an
467
+ export (values and types) that no other file in the TypeScript program
468
+ imports, and a module that exports names but is never imported at all.
469
+
470
+ - Source of truth is the program typescript-eslint already built for the
471
+ type-aware rules: no extra parse, no file enumeration, no filesystem.
472
+ - Per file the rule walks top-level statements, plus every node of a file
473
+ whose text contains `import(`, and caches the result
474
+ on the `ts.SourceFile` object; the project-wide usage map is cached per
475
+ `ts.Program`, and an edit that only touches a few files patches that map
476
+ in place instead of rebuilding it. The patch is scoped per project
477
+ (`tsconfig`): programs from different projects never share an aggregate,
478
+ even when ESLint alternates between them in the same process. Editing one
479
+ file re-walks that file and rebuilds the map once. At 10k files the warm
480
+ cost is map lookups.
481
+ - Re-exports are followed: `export { x } from`, `export * from`, and
482
+ `export * as ns from` count usage at the file that declares `x`, so a
483
+ barrel does not hide a dead export. `import * as ns`, default imports,
484
+ and dynamic `import('./x')` anywhere in the file (a lazy route's
485
+ `loadComponent: () => import('./x')` included) count every export of the
486
+ target as used.
487
+ - Files matching `entryPoints` are never reported (default `**/main.ts`,
488
+ `**/main.*.ts`, `**/public-api.ts`, `**/index.ts`, `**/*.config.ts`,
489
+ `**/*.config.mts`, `**/*.config.cts`, `**/*.spec.ts`, `**/*.spec.util.ts`,
490
+ `**/*.stub.ts`, `**/*.mock.ts`, `**/*.d.ts`, `**/*.stories.ts`,
491
+ `**/environment*.ts`).
492
+ Files with `export =` or a `declare module` block are skipped.
493
+ - Names an entry point re-exports (`export { x } from`, `export * from`)
494
+ are public API and never reported: an Nx library's `index.ts` protects
495
+ the exports other projects consume, even though those projects sit
496
+ outside the library's own TypeScript program.
497
+ - The project is the linted file's tsconfig program. An export used only
498
+ from a file outside that program (for example a spec excluded by
499
+ `tsconfig.lib.json`) reports as unused; that is the same boundary `tsc`
500
+ draws.
501
+
405
502
  ## Stylelint and Prettier presets
406
503
 
407
504
  These are standalone configs exported as subpaths — they are not part of the `recommended` ESLint array.