lint-suite 2.2.0 → 3.0.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/CHANGELOG.md CHANGED
@@ -1,3 +1,92 @@
1
+ # 3.0.0 (2026-10-13)
2
+
3
+ ### 🧱 Updated Dependencies
4
+
5
+ - Updated eslint-arrow-fits-line to 1.0.0
6
+ - Updated eslint-chain-fits-line to 1.0.0
7
+ - Updated eslint-chain-receiver-name to 1.0.0
8
+ - Updated eslint-explicit-accessibility to 1.0.0
9
+ - Updated eslint-max-condition-operands to 1.0.0
10
+ - Updated eslint-modular-boundaries to 1.0.0
11
+ - Updated eslint-no-call-in-condition to 1.0.0
12
+ - Updated eslint-no-grouped-condition to 1.0.0
13
+ - Updated eslint-no-inline-object-types to 1.0.0
14
+ - Updated eslint-no-inline-return-object to 1.0.0
15
+ - Updated eslint-no-nested-object-value to 1.0.0
16
+ - Updated eslint-no-spread-expression to 1.0.0
17
+ - Updated eslint-no-unused-exports to 1.0.0
18
+ - Updated eslint-one-line-guard to 1.0.0
19
+ - Updated eslint-readonly-type-properties to 1.0.0
20
+ - Updated eslint-ternary-branch-shape to 1.0.0
21
+ - Updated eslint-test-file-shape to 1.0.0
22
+ - Updated eslint-type-placement to 1.0.0
23
+ - Updated eslint-util-purity to 1.0.0
24
+ - Updated eslint-no-unused-angular-fields to 1.0.0
25
+ - Updated eslint-no-unstyled-classes to 1.0.0
26
+ - Updated stylelint-no-unused-classes to 2.0.0
27
+
28
+ ## 2.3.0 (2026-10-11)
29
+
30
+ ### 🚀 Features
31
+
32
+ - ⚠️ **lint-suite:** ✨ add workspace project boundaries enforcement ([3c1ca35](https://github.com/F0rty-Tw0/lint-suite/commit/3c1ca35))
33
+ - ⚠️ **lint-suite:** ✨ add new ESLint rules and stylelint configuration ([c004e95](https://github.com/F0rty-Tw0/lint-suite/commit/c004e95))
34
+ - ⚠️ **tests:** ✨ add `reportedRuleIds` utility function for ESLint ([15f4794](https://github.com/F0rty-Tw0/lint-suite/commit/15f4794))
35
+ - ⚠️ **tests:** ✨ add tests for angular, boundaries, and playwright ([5e43275](https://github.com/F0rty-Tw0/lint-suite/commit/5e43275))
36
+ - ⚠️ **lint-suite:** ✨ drop nx's legacy settings from the JS and TS presets ([a63e5e5](https://github.com/F0rty-Tw0/lint-suite/commit/a63e5e5))
37
+ - ⚠️ **lint-suite:** ✨ drop the @nx/eslint-plugin dependency ([79d6b62](https://github.com/F0rty-Tw0/lint-suite/commit/79d6b62))
38
+
39
+ ### ⚠️ Breaking Changes
40
+
41
+ - **lint-suite:** ✨ add workspace project boundaries enforcement ([3c1ca35](https://github.com/F0rty-Tw0/lint-suite/commit/3c1ca35))
42
+ projects tagged `type:lib` or `type:app` cannot
43
+ import each other; untagged projects are unconstrained. See
44
+ migration details in the documentation.
45
+ - **lint-suite:** ✨ add new ESLint rules and stylelint configuration ([c004e95](https://github.com/F0rty-Tw0/lint-suite/commit/c004e95))
46
+ - **tests:** ✨ add `reportedRuleIds` utility function for ESLint ([15f4794](https://github.com/F0rty-Tw0/lint-suite/commit/15f4794))
47
+ - **tests:** ✨ add tests for angular, boundaries, and playwright ([5e43275](https://github.com/F0rty-Tw0/lint-suite/commit/5e43275))
48
+ - **lint-suite:** ✨ drop nx's legacy settings from the JS and TS presets ([a63e5e5](https://github.com/F0rty-Tw0/lint-suite/commit/a63e5e5))
49
+ JS files report the 18 core bug rules and no longer
50
+ get @typescript-eslint/* rules; ecmaVersion is latest; .cjs files get
51
+ CommonJS scoping; @typescript-eslint/no-empty-interface is no longer
52
+ set. Migrations are in RELEASE_NOTES.md.
53
+ - **lint-suite:** ✨ drop the @nx/eslint-plugin dependency ([79d6b62](https://github.com/F0rty-Tw0/lint-suite/commit/79d6b62))
54
+ the @nx plugin is no longer registered; composing
55
+ base/javascript/typescript without prettier now reports formatting
56
+ rules nx's prettier append used to turn off; the default TS
57
+ tsconfigRootDir is process.cwd() instead of the Nx workspace root.
58
+ Migrations are in RELEASE_NOTES.md.
59
+
60
+ ### 🧱 Updated Dependencies
61
+
62
+ - Updated eslint-plugin-arrow-body-fits-line to 1.1.0
63
+ - Updated eslint-plugin-chain-fits-line to 1.1.0
64
+ - Updated eslint-plugin-chain-receiver-is-name to 1.1.0
65
+ - Updated eslint-plugin-explicit-accessibility to 1.1.0
66
+ - Updated eslint-plugin-max-condition-operands to 1.1.0
67
+ - Updated eslint-plugin-modular-boundaries to 1.0.0
68
+ - Updated eslint-plugin-no-call-in-condition to 1.1.0
69
+ - Updated eslint-plugin-no-grouped-condition to 1.1.0
70
+ - Updated eslint-plugin-no-inline-object-types to 1.1.0
71
+ - Updated eslint-plugin-no-inline-return-object to 1.1.0
72
+ - Updated eslint-plugin-no-nested-object-value to 1.1.0
73
+ - Updated eslint-plugin-no-spread-expression to 1.1.0
74
+ - Updated eslint-plugin-no-unused-exports to 1.1.0
75
+ - Updated eslint-plugin-one-line-guard to 1.1.0
76
+ - Updated eslint-plugin-readonly-type-properties to 1.1.0
77
+ - Updated eslint-plugin-ternary-branch-shape to 1.1.0
78
+ - Updated eslint-plugin-test-file-shape to 1.1.0
79
+ - Updated eslint-plugin-type-placement to 1.1.0
80
+ - Updated eslint-plugin-util-purity to 1.1.0
81
+ - Updated eslint-plugin-no-unused-angular-instance-fields to 1.2.0
82
+ - Updated eslint-plugin-no-unstyled-classes to 1.1.0
83
+ - Updated stylelint-no-unused-classes to 1.1.0
84
+
85
+ ### ❤️ Thank You
86
+
87
+ - Artiom Tofan
88
+ - F0rty-Tw0
89
+
1
90
  ## 2.2.0 (2026-09-23)
2
91
 
3
92
  ### 🚀 Features
@@ -62,6 +151,37 @@
62
151
 
63
152
  ## [Unreleased]
64
153
 
154
+ ### Breaking Changes
155
+
156
+ - **Workspace project boundaries**: `boundaries`, and so `recommended`, now
157
+ enables `modular-boundaries/modular-boundaries` from
158
+ `@lint-suite/eslint-plugin-modular-boundaries`. Projects tagged `type:lib`
159
+ or `type:app` cannot import a project tagged `type:app`; untagged projects
160
+ are unconstrained. The rule's default cycle and undeclared-dependency checks
161
+ are on. See `RELEASE_NOTES.md` for migration.
162
+ - **`@nx/eslint-plugin` dropped**: the `@nx` plugin is no longer registered,
163
+ and the `javascript` and `typescript` presets are rebuilt on `@eslint/js`
164
+ and `typescript-eslint` recommended. See `RELEASE_NOTES.md` for the full
165
+ list and migrations.
166
+
167
+ ### Dependencies
168
+
169
+ - `angular-eslint` 22.5.0, `typescript-eslint` 8.71.1, `globals` 17.13.0,
170
+ `@eslint/json` 2.1.1, `eslint-plugin-playwright` 2.12.1,
171
+ `eslint-plugin-storybook` 10.6.1, `stylelint` 17.16.0, `stylelint-scss`
172
+ 7.3.0. The ESLint peer range stays `^10.9.1`.
173
+ - New upstream rules enabled at `error`: typescript-eslint
174
+ `no-generated-empty-object-type` and `no-unsafe-enum-assignment`; Angular
175
+ `reactive-context-must-read-signal` and template `prefer-style-binding`;
176
+ Playwright `no-export`, `no-test-return-statement`,
177
+ `no-template-literal-title`, `prefer-ending-with-an-expect`,
178
+ `require-annotation-reason`, `no-action-timeout` and `no-magic-timeouts`;
179
+ Stylelint `selector-no-unmatchable`.
180
+ - `playwright` now warns on `playwright/no-identical-title`, added to
181
+ `eslint-plugin-playwright`'s `flat/recommended` in 2.12.
182
+
183
+ ## [2.1.1] - 2026-09-11
184
+
65
185
  ### Features
66
186
 
67
187
  - All 21 custom rules are independently installable as scoped ESLint and
package/README.md CHANGED
@@ -24,7 +24,7 @@ The suite includes custom rules for TypeScript code structure, unused exports, u
24
24
  - **Prettier**: Automatic disabling of formatting rules that conflict with Prettier (`eslint-config-prettier`)
25
25
  - **Prettier config**: Standalone formatting preset (subpath `lint-suite/prettier`) with the suite's house defaults and Angular/HTML overrides
26
26
  - **Stylelint**: Standalone SCSS/CSS preset (subpath `lint-suite/stylelint`) with standard + recess-order + BEM selector enforcement and `lint-suite/no-unused-classes`
27
- - **Architecture**: Module boundary enforcement with `eslint-plugin-boundaries`
27
+ - **Architecture**: Folder layer boundaries with `eslint-plugin-boundaries`, and workspace project boundaries by tag with `@lint-suite/eslint-modular-boundaries`
28
28
  - **Additional Support**: JSON (with comment support for tsconfig), Storybook CSF enforcement
29
29
 
30
30
  ## Installation
@@ -35,16 +35,16 @@ pnpm add -D lint-suite
35
35
 
36
36
  ## Standalone custom rules
37
37
 
38
- All 21 custom rules also have independently versioned scoped packages.
38
+ All 22 custom rules also have independently versioned scoped packages.
39
39
  Installing one does not install this umbrella or its unrelated presets.
40
40
  For example:
41
41
 
42
42
  ```bash
43
- pnpm add -D @lint-suite/eslint-plugin-arrow-body-fits-line eslint typescript typescript-eslint
43
+ pnpm add -D @lint-suite/eslint-arrow-fits-line eslint typescript typescript-eslint
44
44
  ```
45
45
 
46
46
  ```js
47
- import arrow from '@lint-suite/eslint-plugin-arrow-body-fits-line';
47
+ import arrow from '@lint-suite/eslint-arrow-fits-line';
48
48
  import tseslint from 'typescript-eslint';
49
49
 
50
50
  export default [
@@ -52,21 +52,18 @@ export default [
52
52
  files: ['**/*.ts'],
53
53
  languageOptions: { parser: tseslint.parser },
54
54
  plugins: { arrow },
55
- rules: { 'arrow/arrow-body-fits-line': 'error' }
55
+ rules: { 'arrow/arrow-fits-line': 'error' }
56
56
  }
57
57
  ];
58
58
  ```
59
59
 
60
60
  The [package catalog](https://github.com/F0rty-Tw0/lint-suite#standalone-custom-rules)
61
- lists every package and its rule key. The Angular instance-field package is
62
- `@lint-suite/eslint-plugin-no-unused-angular-instance-fields`; its rule key
63
- remains `no-unused-instance-fields`. The Stylelint package is
61
+ lists every package and its rule key. Each ESLint rule key matches its
62
+ package name without the `@lint-suite/eslint-` prefix. The Stylelint package is
64
63
  `@lint-suite/stylelint-no-unused-classes`, with rule ID
65
64
  `lint-suite/no-unused-classes`.
66
65
 
67
- No migration is needed for existing umbrella users: its three entrypoints,
68
- presets, rule IDs, severities, and options are unchanged. Avoid enabling a
69
- standalone rule alongside the corresponding umbrella rule.
66
+ Avoid enabling a standalone rule alongside the corresponding umbrella rule.
70
67
 
71
68
  ## Dependencies
72
69
 
@@ -135,7 +132,7 @@ export default [
135
132
  | Configuration | Description |
136
133
  | ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
137
134
  | `base` | Core JavaScript rules, formatting, and complexity limits |
138
- | `javascript` | JavaScript-specific rules via `@nx/eslint-plugin` |
135
+ | `javascript` | JavaScript-specific rules (`@eslint/js` recommended) |
139
136
  | `typescript` | TypeScript strict typing, imports, and naming conventions |
140
137
  | `angular` | Angular component best practices with Signal support |
141
138
  | `angularTemplate` | HTML template rules with accessibility, performance, and `lint-suite-angular-template/no-unstyled-classes` |
@@ -144,12 +141,12 @@ export default [
144
141
  | `playwright` | Playwright e2e locator and matcher best practices |
145
142
  | `json` | JSON linting with comment support for tsconfig/vscode |
146
143
  | `storybook` | Storybook CSF enforcement |
147
- | `boundaries` | Module boundary rules (feature, data-access, ui, etc.) |
144
+ | `boundaries` | Folder layer rules (feature, data-access, ui, etc.) and workspace project rules by tag (`type:app` is a leaf) |
148
145
  | `prettier` | Disables rules that conflict with Prettier (use last) |
149
146
  | **`recommended`** | **Baseline only: `base` + `javascript` + `typescript` + `json` + `boundaries` + `prettier` — compose the rest on top** |
150
147
 
151
148
  **Angular project analysis:** The `angular` preset enables `projectService:
152
- true` and project analysis for `lint-suite-angular/no-unused-instance-fields`.
149
+ true` and project analysis for `lint-suite-angular/no-unused-angular-fields`.
153
150
  It counts exact reads in the configured TypeScript/Angular Program, including
154
151
  external parent templates/TypeScript, subclasses, and Angular interface
155
152
  implementations; code outside that Program is unknowable. Project mode also
@@ -194,9 +191,61 @@ export default [
194
191
  ];
195
192
  ```
196
193
 
194
+ ### Workspace project boundaries
195
+
196
+ The `boundaries` config enables `modular-boundaries/modular-boundaries` from
197
+ [`@lint-suite/eslint-modular-boundaries`](https://github.com/F0rty-Tw0/lint-suite/tree/master/packages/eslint/modular-boundaries#readme).
198
+ It reads pnpm, npm, Nx, Turborepo and moon workspace files and checks imports
199
+ between workspace projects. Outside a workspace it does nothing.
200
+
201
+ The preset's constraints make `type:app` projects leaves: a project tagged
202
+ `type:lib` or `type:app` cannot import a project tagged `type:app`. Untagged
203
+ projects are unconstrained. Tag a project in `project.json` `tags`,
204
+ `package.json` `nx.tags`, or a top-level `package.json` `tags` array:
205
+
206
+ ```json
207
+ { "name": "@acme/web", "tags": ["type:app"] }
208
+ ```
209
+
210
+ The rule's default checks also run: cycles between projects, relative imports
211
+ into another project, self-imports by package name, and packages a project's
212
+ own `package.json` does not declare. That last check includes tooling
213
+ installed only at the workspace root, such as `vitest` imported from a
214
+ package's specs; declare it in the package's `devDependencies`, or set
215
+ `checkUndeclaredDependencies: false`.
216
+
217
+ To change the options, set the rule again in a later entry. ESLint replaces
218
+ options rather than merging them, so repeat the `depConstraints` you keep:
219
+
220
+ ```js
221
+ import { recommended } from 'lint-suite/eslint';
222
+
223
+ export default [
224
+ ...recommended,
225
+ {
226
+ rules: {
227
+ 'modular-boundaries/modular-boundaries': [
228
+ 'error',
229
+ {
230
+ depConstraints: [
231
+ { sourceTag: 'type:lib', notDependOnLibsWithTags: ['type:app'] },
232
+ { sourceTag: 'type:app', notDependOnLibsWithTags: ['type:app'] },
233
+ { sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }
234
+ ],
235
+ checkUndeclaredDependencies: false
236
+ }
237
+ ]
238
+ }
239
+ }
240
+ ];
241
+ ```
242
+
243
+ With `eslint --cache`, a cycle created by editing one file is not reported in
244
+ other, unchanged files until they are linted again.
245
+
197
246
  ### Unused Angular instance fields
198
247
 
199
- The `angular` config enables `lint-suite-angular/no-unused-instance-fields`.
248
+ The `angular` config enables `lint-suite-angular/no-unused-angular-fields`.
200
249
  Project analysis is the default and recognizes reads from other TypeScript
201
250
  files and Angular templates. It requires parser services with type information:
202
251
 
@@ -210,7 +259,7 @@ files and Angular templates. It requires parser services with type information:
210
259
  }
211
260
  },
212
261
  rules: {
213
- 'lint-suite-angular/no-unused-instance-fields': [
262
+ 'lint-suite-angular/no-unused-angular-fields': [
214
263
  'error',
215
264
  {
216
265
  analysis: 'project',
@@ -523,9 +572,9 @@ offers an IDE suggestion with a placeholder name instead of an auto-fix.
523
572
  | `local/max-condition-operands` | An `if` condition with more than `max` (default 3) operands joined by `&&` / `\|\|`. | none |
524
573
  | `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 |
525
574
  | `local/ternary-branch-shape` | A ternary branch that is not a name, literal, template literal, or plain member access. | none |
526
- | `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 |
575
+ | `local/chain-receiver-name` | A member chain starting on an inline expression: `(a ?? b).x`, `{...}.x`, `[...].x`, `(await p).x`. | Suggestion: name the receiver |
527
576
  | `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 |
528
- | `local/arrow-body-fits-line` | An expression-bodied arrow whose body wraps onto more lines. | Fix: block body with `return` |
577
+ | `local/arrow-fits-line` | An expression-bodied arrow whose body wraps onto more lines. | Fix: block body with `return` |
529
578
  | `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` |
530
579
  | `local/no-spread-expression` | `...(expr)` where the argument is not a name or member access. | Suggestion: hoist to a `const` |
531
580
  | `local/no-inline-return-object` | `return {...}` and `=> ({...})`. | Suggestion: `const result = {...}; return result;` |
@@ -707,12 +756,6 @@ The Prettier preset is published with `prettier` as a peer dependency. The Style
707
756
 
708
757
  ## Available Rules (you can add more as you prefer)
709
758
 
710
- ### nx/eslint-plugin
711
-
712
- - `@nx/enforce-module-boundaries`: Enforces module boundary restrictions
713
- - `@nx/dependency-checks`: Validates dependencies in workspace projects
714
- - ...
715
-
716
759
  ### @vitest/eslint-plugin
717
760
 
718
761
  - `vitest/max-nested-describe`: Limits describe nesting depth
@@ -775,6 +818,7 @@ The Prettier preset is published with `prettier` as a peer dependency. The Style
775
818
  - `plugin/selector-bem-pattern`: enforces BEM selectors, treats `*.component.scss`/`*.component.css` as implicit components, ignores `--mdc`/`--sys` custom properties
776
819
  - `lint-suite/no-unused-classes`: reports a class selector no template of the component uses (see [No unused classes](#no-unused-classes))
777
820
  - `no-descending-specificity`: disabled
821
+ - `selector-no-unmatchable`: reports selectors that can never match, such as a pseudo-element inside `:is()`
778
822
 
779
823
  ### prettier (format config)
780
824
 
package/eslint.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // packages/lint-suite/src/lib/angular-template.ts
2
- import lintSuiteAngularTemplatePlugin from "@lint-suite/eslint-plugin-no-unstyled-classes";
2
+ import lintSuiteAngularTemplatePlugin from "@lint-suite/eslint-no-unstyled-classes";
3
3
  import { configs } from "angular-eslint";
4
4
  import { defineConfig } from "eslint/config";
5
5
  var angularTemplate = defineConfig([
@@ -50,6 +50,8 @@ var angularTemplate = defineConfig([
50
50
  // v21: Catch incomplete refactoring
51
51
  "@angular-eslint/template/prefer-class-binding": "error",
52
52
  // v21.2.0: Prefer [class] over ngClass
53
+ "@angular-eslint/template/prefer-style-binding": "error",
54
+ // v22.4: Prefer [style] over ngStyle
53
55
  "@angular-eslint/template/prefer-static-string-properties": "error",
54
56
  // Reduce change detection
55
57
  "@angular-eslint/template/no-non-null-assertion": "error",
@@ -86,7 +88,7 @@ var angularTemplate = defineConfig([
86
88
  ]);
87
89
 
88
90
  // packages/lint-suite/src/lib/angular.ts
89
- import noUnusedAngularInstanceFieldPlugin from "@lint-suite/eslint-plugin-no-unused-angular-instance-fields";
91
+ import noUnusedAngularInstanceFieldPlugin from "@lint-suite/eslint-no-unused-angular-fields";
90
92
  import { configs as configs2, processInlineTemplates } from "angular-eslint";
91
93
  import { defineConfig as defineConfig2 } from "eslint/config";
92
94
  import globals from "globals";
@@ -116,7 +118,7 @@ var angular = defineConfig2([
116
118
  // are dead.
117
119
  // Bad: private unused = 1; read nowhere
118
120
  // Good: delete it, or read it in the template
119
- "lint-suite-angular/no-unused-instance-fields": [
121
+ "lint-suite-angular/no-unused-angular-fields": [
120
122
  "error",
121
123
  { analysis: "project" }
122
124
  ],
@@ -139,6 +141,8 @@ var angular = defineConfig2([
139
141
  // Consistent lifecycle method ordering
140
142
  "@angular-eslint/computed-must-return": "error",
141
143
  // Catch silent computed() bugs missing a return
144
+ "@angular-eslint/reactive-context-must-read-signal": "error",
145
+ // v22.3: computed/linkedSignal/effect that reads no signal never re-runs
142
146
  "@angular-eslint/no-duplicates-in-metadata-arrays": "error",
143
147
  // Catch duplicate imports/providers in @Component/@NgModule
144
148
  "@angular-eslint/no-lifecycle-call": "error",
@@ -359,12 +363,10 @@ var basePractices = defineConfig4([
359
363
  ]);
360
364
 
361
365
  // packages/lint-suite/src/lib/base-recommended.ts
362
- import { configs as configs3 } from "@nx/eslint-plugin";
363
366
  import stylistic from "@stylistic/eslint-plugin";
364
367
  import { defineConfig as defineConfig5 } from "eslint/config";
365
368
  import importPluginX from "eslint-plugin-import-x";
366
369
  var baseRecommended = defineConfig5([
367
- { extends: [...configs3["flat/base"]] },
368
370
  {
369
371
  files: [
370
372
  "**/*.js",
@@ -401,6 +403,7 @@ var base = defineConfig6([
401
403
  ]);
402
404
 
403
405
  // packages/lint-suite/src/lib/boundaries.ts
406
+ import modularBoundariesPlugin from "@lint-suite/eslint-modular-boundaries";
404
407
  import { defineConfig as defineConfig7 } from "eslint/config";
405
408
  import boundariesPlugin from "eslint-plugin-boundaries";
406
409
  var boundaries = defineConfig7([
@@ -414,7 +417,8 @@ var boundaries = defineConfig7([
414
417
  "**/*.mts"
415
418
  ],
416
419
  plugins: {
417
- boundaries: boundariesPlugin
420
+ boundaries: boundariesPlugin,
421
+ "modular-boundaries": modularBoundariesPlugin
418
422
  },
419
423
  settings: {
420
424
  "boundaries/elements": [
@@ -450,6 +454,20 @@ var boundaries = defineConfig7([
450
454
  }
451
455
  },
452
456
  rules: {
457
+ // Workspace projects tagged type:app are leaves: no lib or app imports
458
+ // them. The '*' entry keeps untagged projects free to import.
459
+ // Bad: libs/ui (type:lib) imports apps/web (type:app)
460
+ // Good: move the shared code from apps/web into a type:lib project
461
+ "modular-boundaries/modular-boundaries": [
462
+ "error",
463
+ {
464
+ depConstraints: [
465
+ { sourceTag: "type:lib", notDependOnLibsWithTags: ["type:app"] },
466
+ { sourceTag: "type:app", notDependOnLibsWithTags: ["type:app"] },
467
+ { sourceTag: "*", onlyDependOnLibsWithTags: ["*"] }
468
+ ]
469
+ }
470
+ ],
453
471
  ...boundariesPlugin.configs.recommended.rules,
454
472
  "boundaries/dependencies": [
455
473
  "error",
@@ -517,13 +535,37 @@ var boundaries = defineConfig7([
517
535
  ]);
518
536
 
519
537
  // packages/lint-suite/src/lib/javascript.ts
520
- import { configs as configs4 } from "@nx/eslint-plugin";
538
+ import js from "@eslint/js";
521
539
  import { defineConfig as defineConfig8 } from "eslint/config";
522
- var nxJavascript = configs4["flat/javascript"];
523
- var javascript = defineConfig8({
524
- files: ["**/*.js", "**/*.jsx", "**/*.cjs", "**/*.mjs"],
525
- extends: nxJavascript
526
- });
540
+ import globals2 from "globals";
541
+ import tseslint from "typescript-eslint";
542
+ var javascript = defineConfig8(
543
+ {
544
+ files: ["**/*.js", "**/*.jsx", "**/*.cjs", "**/*.mjs"],
545
+ extends: [js.configs.recommended],
546
+ // Parser and plugin stay so consumer @typescript-eslint/* rules set without `files` still run on JS.
547
+ plugins: { "@typescript-eslint": tseslint.plugin },
548
+ languageOptions: {
549
+ parser: tseslint.parser,
550
+ globals: { ...globals2.browser, ...globals2.node }
551
+ },
552
+ rules: {
553
+ "no-unused-expressions": "error",
554
+ "no-array-constructor": "error",
555
+ "no-empty-function": "error",
556
+ "prefer-rest-params": "error",
557
+ "prefer-spread": "error",
558
+ "no-var": "error",
559
+ "prefer-const": ["error", { destructuring: "all" }]
560
+ }
561
+ },
562
+ {
563
+ files: ["**/*.cjs"],
564
+ languageOptions: {
565
+ parserOptions: { ecmaFeatures: { globalReturn: true } }
566
+ }
567
+ }
568
+ );
527
569
 
528
570
  // packages/lint-suite/src/lib/json.ts
529
571
  import jsonPlugin from "@eslint/json";
@@ -559,7 +601,15 @@ var playwright = defineConfig10([
559
601
  "playwright/prefer-comparison-matcher": "error",
560
602
  // Test structure
561
603
  "playwright/prefer-hooks-on-top": "error",
562
- "playwright/require-top-level-describe": "warn"
604
+ "playwright/require-top-level-describe": "warn",
605
+ "playwright/no-export": "error",
606
+ "playwright/no-test-return-statement": "error",
607
+ "playwright/no-template-literal-title": "error",
608
+ "playwright/prefer-ending-with-an-expect": "error",
609
+ "playwright/require-annotation-reason": "error",
610
+ // Timeouts
611
+ "playwright/no-action-timeout": "error",
612
+ "playwright/no-magic-timeouts": "error"
563
613
  }
564
614
  }
565
615
  ]);
@@ -811,6 +861,7 @@ var typescriptOverrides = defineConfig16([
811
861
  "**/*.e2e.js"
812
862
  ],
813
863
  rules: {
864
+ "no-empty-function": "off",
814
865
  "@typescript-eslint/no-empty-function": "off",
815
866
  "@typescript-eslint/unbound-method": "off",
816
867
  "@typescript-eslint/consistent-type-assertions": "off"
@@ -826,14 +877,22 @@ var typescriptOverrides = defineConfig16([
826
877
  ]);
827
878
 
828
879
  // packages/lint-suite/src/lib/typescript-recommended.ts
829
- import { configs as configs5 } from "@nx/eslint-plugin";
880
+ import js2 from "@eslint/js";
830
881
  import { defineConfig as defineConfig17 } from "eslint/config";
831
882
  import importPluginX2 from "eslint-plugin-import-x";
832
- var nxTypescript = configs5["flat/typescript"];
883
+ import tseslint2 from "typescript-eslint";
833
884
  var typescriptRecommended = defineConfig17([
834
885
  {
835
886
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
836
- extends: [importPluginX2.flatConfigs.typescript, ...nxTypescript]
887
+ extends: [
888
+ importPluginX2.flatConfigs.typescript,
889
+ js2.configs.recommended,
890
+ ...tseslint2.configs.recommended,
891
+ {
892
+ languageOptions: { parserOptions: { tsconfigRootDir: process.cwd() } }
893
+ },
894
+ { rules: { "@typescript-eslint/adjacent-overload-signatures": "error" } }
895
+ ]
837
896
  }
838
897
  ]);
839
898
 
@@ -841,29 +900,29 @@ var typescriptRecommended = defineConfig17([
841
900
  import { defineConfig as defineConfig18 } from "eslint/config";
842
901
 
843
902
  // packages/lint-suite/src/lib/typescript-local-plugin.ts
844
- import { arrowBodyFitsLineRule } from "@lint-suite/eslint-plugin-arrow-body-fits-line";
845
- import { chainFitsLineRule } from "@lint-suite/eslint-plugin-chain-fits-line";
846
- import { chainReceiverIsNameRule } from "@lint-suite/eslint-plugin-chain-receiver-is-name";
847
- import { explicitAccessibilityRule } from "@lint-suite/eslint-plugin-explicit-accessibility";
848
- import { maxConditionOperandsRule } from "@lint-suite/eslint-plugin-max-condition-operands";
849
- import { noCallInConditionRule } from "@lint-suite/eslint-plugin-no-call-in-condition";
850
- import { noGroupedConditionRule } from "@lint-suite/eslint-plugin-no-grouped-condition";
851
- import { noInlineObjectTypesRule } from "@lint-suite/eslint-plugin-no-inline-object-types";
852
- import { noInlineReturnObjectRule } from "@lint-suite/eslint-plugin-no-inline-return-object";
853
- import { noNestedObjectValueRule } from "@lint-suite/eslint-plugin-no-nested-object-value";
854
- import { noSpreadExpressionRule } from "@lint-suite/eslint-plugin-no-spread-expression";
855
- import { noUnusedExportsRule } from "@lint-suite/eslint-plugin-no-unused-exports";
856
- import { oneLineGuardRule } from "@lint-suite/eslint-plugin-one-line-guard";
857
- import { readonlyTypePropertiesRule } from "@lint-suite/eslint-plugin-readonly-type-properties";
858
- import { ternaryBranchShapeRule } from "@lint-suite/eslint-plugin-ternary-branch-shape";
859
- import { testFileShapeRule } from "@lint-suite/eslint-plugin-test-file-shape";
860
- import { typePlacementRule } from "@lint-suite/eslint-plugin-type-placement";
861
- import { utilPurityRule } from "@lint-suite/eslint-plugin-util-purity";
903
+ import { arrowFitsLineRule } from "@lint-suite/eslint-arrow-fits-line";
904
+ import { chainFitsLineRule } from "@lint-suite/eslint-chain-fits-line";
905
+ import { chainReceiverNameRule } from "@lint-suite/eslint-chain-receiver-name";
906
+ import { explicitAccessibilityRule } from "@lint-suite/eslint-explicit-accessibility";
907
+ import { maxConditionOperandsRule } from "@lint-suite/eslint-max-condition-operands";
908
+ import { noCallInConditionRule } from "@lint-suite/eslint-no-call-in-condition";
909
+ import { noGroupedConditionRule } from "@lint-suite/eslint-no-grouped-condition";
910
+ import { noInlineObjectTypesRule } from "@lint-suite/eslint-no-inline-object-types";
911
+ import { noInlineReturnObjectRule } from "@lint-suite/eslint-no-inline-return-object";
912
+ import { noNestedObjectValueRule } from "@lint-suite/eslint-no-nested-object-value";
913
+ import { noSpreadExpressionRule } from "@lint-suite/eslint-no-spread-expression";
914
+ import { noUnusedExportsRule } from "@lint-suite/eslint-no-unused-exports";
915
+ import { oneLineGuardRule } from "@lint-suite/eslint-one-line-guard";
916
+ import { readonlyTypePropertiesRule } from "@lint-suite/eslint-readonly-type-properties";
917
+ import { ternaryBranchShapeRule } from "@lint-suite/eslint-ternary-branch-shape";
918
+ import { testFileShapeRule } from "@lint-suite/eslint-test-file-shape";
919
+ import { typePlacementRule } from "@lint-suite/eslint-type-placement";
920
+ import { utilPurityRule } from "@lint-suite/eslint-util-purity";
862
921
  var meta = { name: "local" };
863
922
  var rules = {
864
- "arrow-body-fits-line": arrowBodyFitsLineRule,
923
+ "arrow-fits-line": arrowFitsLineRule,
865
924
  "chain-fits-line": chainFitsLineRule,
866
- "chain-receiver-is-name": chainReceiverIsNameRule,
925
+ "chain-receiver-name": chainReceiverNameRule,
867
926
  "explicit-accessibility": explicitAccessibilityRule,
868
927
  "max-condition-operands": maxConditionOperandsRule,
869
928
  "no-call-in-condition": noCallInConditionRule,
@@ -891,13 +950,13 @@ var typescriptSafety = defineConfig18([
891
950
  rules: {
892
951
  // Expression arrow whose body wraps gets a block body. Fixes.
893
952
  // Bad: (x) => a && b on two lines Good: (x) => { return a && b; }
894
- "local/arrow-body-fits-line": "error",
953
+ "local/arrow-fits-line": "error",
895
954
  // Two or more calls in one chain stay on one line.
896
955
  // Bad: a .b() .c() over three lines Good: const b = a.b(); b.c();
897
956
  "local/chain-fits-line": "error",
898
957
  // A member chain starts on a name, never on an inline expression.
899
958
  // Bad: (a ?? b).c Good: const ab = a ?? b; ab.c
900
- "local/chain-receiver-is-name": "error",
959
+ "local/chain-receiver-name": "error",
901
960
  // Every class member states public, private, or protected. Fixes.
902
961
  // Bad: field = 1; Good: private field = 1;
903
962
  "local/explicit-accessibility": "error",
@@ -975,6 +1034,10 @@ var typescriptSafety = defineConfig18([
975
1034
  "@typescript-eslint/no-unsafe-return": "error",
976
1035
  "@typescript-eslint/no-unsafe-argument": "error",
977
1036
  "@typescript-eslint/no-empty-object-type": "error",
1037
+ "@typescript-eslint/no-generated-empty-object-type": "error",
1038
+ // v8.70: type operations resolving to {}
1039
+ "@typescript-eslint/no-unsafe-enum-assignment": "error",
1040
+ // v8.71: non-enum value where an enum is expected
978
1041
  "@typescript-eslint/no-unsafe-function-type": "error",
979
1042
  "@typescript-eslint/no-wrapper-object-types": "error",
980
1043
  "@typescript-eslint/explicit-module-boundary-types": "error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lint-suite",
3
- "version": "2.2.0",
3
+ "version": "3.0.0",
4
4
  "description": "Reusable ESLint, Stylelint, and Prettier configurations",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,49 +25,49 @@
25
25
  "author": "Artiom Tofan artiomtofancv@gmail.com",
26
26
  "type": "module",
27
27
  "dependencies": {
28
- "@lint-suite/eslint-plugin-arrow-body-fits-line": "1.0.0",
29
- "@lint-suite/eslint-plugin-chain-fits-line": "1.0.0",
30
- "@lint-suite/eslint-plugin-chain-receiver-is-name": "1.0.0",
31
- "@lint-suite/eslint-plugin-explicit-accessibility": "1.0.0",
32
- "@lint-suite/eslint-plugin-max-condition-operands": "1.0.0",
33
- "@lint-suite/eslint-plugin-no-call-in-condition": "1.0.0",
34
- "@lint-suite/eslint-plugin-no-grouped-condition": "1.0.0",
35
- "@lint-suite/eslint-plugin-no-inline-object-types": "1.0.0",
36
- "@lint-suite/eslint-plugin-no-inline-return-object": "1.0.0",
37
- "@lint-suite/eslint-plugin-no-nested-object-value": "1.0.0",
38
- "@lint-suite/eslint-plugin-no-spread-expression": "1.0.0",
39
- "@lint-suite/eslint-plugin-no-unused-exports": "1.0.0",
40
- "@lint-suite/eslint-plugin-one-line-guard": "1.0.0",
41
- "@lint-suite/eslint-plugin-readonly-type-properties": "1.0.0",
42
- "@lint-suite/eslint-plugin-ternary-branch-shape": "1.0.0",
43
- "@lint-suite/eslint-plugin-test-file-shape": "1.0.0",
44
- "@lint-suite/eslint-plugin-type-placement": "1.0.0",
45
- "@lint-suite/eslint-plugin-util-purity": "1.0.0",
46
- "@lint-suite/eslint-plugin-no-unused-angular-instance-fields": "1.1.0",
47
- "@lint-suite/eslint-plugin-no-unstyled-classes": "1.0.0",
48
- "@lint-suite/stylelint-no-unused-classes": "1.0.0",
28
+ "@lint-suite/eslint-arrow-fits-line": "1.0.0",
29
+ "@lint-suite/eslint-chain-fits-line": "1.0.0",
30
+ "@lint-suite/eslint-chain-receiver-name": "1.0.0",
31
+ "@lint-suite/eslint-explicit-accessibility": "1.0.0",
32
+ "@lint-suite/eslint-max-condition-operands": "1.0.0",
33
+ "@lint-suite/eslint-modular-boundaries": "1.0.0",
34
+ "@lint-suite/eslint-no-call-in-condition": "1.0.0",
35
+ "@lint-suite/eslint-no-grouped-condition": "1.0.0",
36
+ "@lint-suite/eslint-no-inline-object-types": "1.0.0",
37
+ "@lint-suite/eslint-no-inline-return-object": "1.0.0",
38
+ "@lint-suite/eslint-no-nested-object-value": "1.0.0",
39
+ "@lint-suite/eslint-no-spread-expression": "1.0.0",
40
+ "@lint-suite/eslint-no-unused-exports": "1.0.0",
41
+ "@lint-suite/eslint-one-line-guard": "1.0.0",
42
+ "@lint-suite/eslint-readonly-type-properties": "1.0.0",
43
+ "@lint-suite/eslint-ternary-branch-shape": "1.0.0",
44
+ "@lint-suite/eslint-test-file-shape": "1.0.0",
45
+ "@lint-suite/eslint-type-placement": "1.0.0",
46
+ "@lint-suite/eslint-util-purity": "1.0.0",
47
+ "@lint-suite/eslint-no-unused-angular-fields": "1.0.0",
48
+ "@lint-suite/eslint-no-unstyled-classes": "1.0.0",
49
+ "@lint-suite/stylelint-no-unused-classes": "2.0.0",
49
50
  "@eslint/js": "^10.0.1",
50
- "@eslint/json": "^2.1.0",
51
- "@nx/eslint-plugin": "23.2.0",
51
+ "@eslint/json": "^2.1.1",
52
52
  "@smarttools/eslint-plugin-rxjs": "1.0.22",
53
53
  "@stylistic/eslint-plugin": "5.10.0",
54
54
  "@vitest/eslint-plugin": "1.6.27",
55
- "angular-eslint": "^22.2.0",
55
+ "angular-eslint": "^22.5.0",
56
56
  "eslint": "^10.9.1",
57
57
  "eslint-config-prettier": "^10.1.8",
58
58
  "eslint-import-resolver-typescript": "4.4.5",
59
59
  "eslint-plugin-boundaries": "7.2.0",
60
60
  "eslint-plugin-import-x": "4.17.1",
61
- "eslint-plugin-playwright": "2.11.0",
62
- "eslint-plugin-storybook": "10.6.0",
63
- "globals": "17.12.0",
64
- "stylelint": "17.14.1",
61
+ "eslint-plugin-playwright": "2.12.1",
62
+ "eslint-plugin-storybook": "10.6.1",
63
+ "globals": "17.13.0",
64
+ "stylelint": "17.16.0",
65
65
  "stylelint-config-recess-order": "7.8.0",
66
66
  "stylelint-config-standard": "40.0.0",
67
67
  "stylelint-config-standard-scss": "17.0.0",
68
- "stylelint-scss": "7.2.0",
68
+ "stylelint-scss": "7.3.0",
69
69
  "stylelint-selector-bem-pattern": "5.0.0",
70
- "typescript-eslint": "8.69.0"
70
+ "typescript-eslint": "8.71.1"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "prettier": "^3.9.6",
package/stylelint.js CHANGED
@@ -28,6 +28,7 @@ var stylelint = {
28
28
  ignoreCustomProperties: ["^--mdc", "^--sys"]
29
29
  },
30
30
  "no-descending-specificity": null,
31
+ "selector-no-unmatchable": true,
31
32
  // A stylesheet class no linked template uses is reported.
32
33
  // Bad: .ghost {} used by no template
33
34
  // Good: delete it, or use it in a template