lint-suite 1.2.2 → 1.3.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
@@ -4,61 +4,76 @@ A comprehensive collection of ESLint Flat configurations for modern web applicat
4
4
 
5
5
  ## Features
6
6
 
7
- - **TypeScript Linting**: Strict typing rules, consistent imports, and code organization
8
- - **Angular Support**: Component best practices (including Signals), template accessibility rules, and modern control flow
9
- - **RxJS Guidelines**: Observable patterns, Finnish notation, and operator safety
7
+ - **TypeScript Linting**: Strict typing rules, v8 type-safety replacements, consistent imports, and code organization
8
+ - **Angular Support**: Component best practices (including Signals), Angular 21+ template rules, and modern control flow
9
+ - **RxJS Guidelines**: Observable patterns, Finnish notation, subject encapsulation, and operator safety
10
10
  - **Code Style**: Formatting rules, line limits, and structural consistency
11
11
  - **Accessibility**: ARIA validation, keyboard events, and semantic HTML
12
- - **Testing**: Jest configurations with appropriate relaxations for test files
13
- - **Additional Support**: JSON, Storybook, and more
12
+ - **Testing**: Jest, Vitest, and Playwright configurations with best-practice rules
13
+ - **Prettier**: Automatic disabling of formatting rules that conflict with Prettier
14
+ - **Architecture**: Module boundary enforcement with `eslint-plugin-boundaries`
15
+ - **Additional Support**: JSON (with comment support for tsconfig), Storybook CSF enforcement
14
16
 
15
17
  ## Installation
16
18
 
17
19
  ```bash
18
- npm install --save-dev lint-suite
20
+ pnpm add -D lint-suite
19
21
  ```
20
22
 
21
23
  ## Dependencies
22
24
 
23
25
  ```bash
24
- npm install --save-dev eslint typescript-eslint eslint-config-prettier
26
+ pnpm add -D eslint typescript-eslint eslint-config-prettier
25
27
  ```
26
28
 
27
29
  ## Usage
28
30
 
29
- Create a `.eslintrc.config.mjs` file in your project root:
31
+ Create an `eslint.config.mjs` file in your project root:
30
32
 
31
33
  ```js
32
- import { base, javascript, typescript } from 'lint-suite';
34
+ import { recommended } from 'lint-suite';
35
+
36
+ export default [...recommended];
37
+ ```
38
+
39
+ Or selectively include configurations:
40
+
41
+ ```js
42
+ import { base, javascript, typescript, prettier } from 'lint-suite';
33
43
 
34
44
  export default [
35
45
  ...base,
36
46
  ...javascript,
37
- ...typescript
38
- // Rest of the required configurations
47
+ ...typescript,
48
+ ...prettier // Must be last to disable conflicting formatting rules
39
49
  ];
40
50
  ```
41
51
 
42
- You can selectively include configurations you need.
43
-
44
52
  ## Available Configurations
45
53
 
46
- - `base`: Core JavaScript rules and formatting
47
- - `typescript`: TypeScript-specific rules
48
- - `angular`: Angular component best practices
49
- - `angularTemplate`: HTML template rules with accessibility focus
50
- - `rxjs`: Observable patterns and operator safety
51
- - `jest`: Testing configurations
52
- - `json`: JSON file linting
53
- - `storybook`: Storybook support
54
- - `boundaries`: Enforce module boundaries (feature, data-access, ui, etc.)
54
+ | Configuration | Description |
55
+ | ------------------- | -------------------------------------------------------- |
56
+ | `base` | Core JavaScript rules, formatting, and complexity limits |
57
+ | `javascript` | JavaScript-specific rules via `@nx/eslint-plugin` |
58
+ | `typescript` | TypeScript strict typing, imports, and naming conventions |
59
+ | `angular` | Angular component best practices with Signal support |
60
+ | `angularTemplate` | HTML template rules with accessibility and performance |
61
+ | `rxjs` | Observable patterns, operator safety, and subscriptions |
62
+ | `jest` | Jest testing best practices and matchers |
63
+ | `vitest` | Vitest testing rules and matcher improvements |
64
+ | `playwright` | Playwright e2e locator and matcher best practices |
65
+ | `json` | JSON linting with comment support for tsconfig/vscode |
66
+ | `storybook` | Storybook CSF enforcement |
67
+ | `boundaries` | Module boundary rules (feature, data-access, ui, etc.) |
68
+ | `prettier` | Disables rules that conflict with Prettier (use last) |
69
+ | **`recommended`** | **All of the above combined in the correct order** |
55
70
 
56
71
  ## Customization
57
72
 
58
73
  You can override any rules by adding a `rules` section to your ESLint config:
59
74
 
60
75
  ```js
61
- import { typescript } from 'lint-suite';
76
+ import { typescript, prettier } from 'lint-suite';
62
77
 
63
78
  export default [
64
79
  ...typescript,
@@ -66,7 +81,8 @@ export default [
66
81
  rules: {
67
82
  '@typescript-eslint/explicit-function-return-type': 'off'
68
83
  }
69
- }
84
+ },
85
+ ...prettier
70
86
  ];
71
87
  ```
72
88
 
@@ -80,9 +96,24 @@ export default [
80
96
 
81
97
  ### eslint-plugin-jest
82
98
 
83
- - `jest/no-disabled-tests`: Disallows disabled tests
84
- - `jest/expect-expect`: Ensures test cases contain at least one expect statement
85
- - `jest/valid-expect`: Validates expect() usage
99
+ - `jest/consistent-test-it`: Enforces consistent test function usage (`it`)
100
+ - `jest/prefer-comparison-matcher`: Enforces comparison matchers
101
+ - `jest/prefer-hooks-on-top`: Ensures hooks are at the top of describe blocks
102
+ - `jest/no-conditional-in-test`: Warns against conditionals in tests
103
+ - ...
104
+
105
+ ### @vitest/eslint-plugin
106
+
107
+ - `vitest/max-nested-describe`: Limits describe nesting depth
108
+ - `vitest/prefer-to-be`: Enforces `toBe` matcher usage
109
+ - `vitest/no-conditional-in-test`: Disallows conditionals in tests
110
+ - ...
111
+
112
+ ### eslint-plugin-playwright
113
+
114
+ - `playwright/prefer-locator`: Enforces modern locator API
115
+ - `playwright/prefer-native-locators`: Prefers native locator methods
116
+ - `playwright/prefer-to-be`: Enforces `toBe` matcher usage
86
117
  - ...
87
118
 
88
119
  ### eslint-plugin-json
@@ -94,22 +125,23 @@ export default [
94
125
  ### @smarttools/eslint-plugin-rxjs
95
126
 
96
127
  - `@rxjs/finnish`: Enforces Finnish notation for observables
97
- - `@rxjs/no-ignored-subscription`: Prevents ignoring returned subscriptions
128
+ - `@rxjs/no-exposed-subjects`: Enforces subject encapsulation
129
+ - `@rxjs/no-cyclic-action`: Prevents infinite loops in NgRx effects
98
130
  - `@rxjs/no-unsafe-takeuntil`: Ensures proper usage of takeUntil operator
99
131
  - ...
100
132
 
101
133
  ### eslint-plugin-storybook
102
134
 
103
- - `storybook/no-redundant-story-name`: Prevents redundant story names
104
- - `storybook/csf-component`: Enforces Component Story Format standards
105
- - `storybook/hierarchy-separator`: Enforces story hierarchy separators
135
+ - `storybook/csf-component`: Enforces component property in stories
136
+ - `storybook/no-stories-of`: Prevents deprecated `storiesOf` API
106
137
  - ...
107
138
 
108
- ### eslint-plugin-import
139
+ ### eslint-plugin-import-x
109
140
 
110
- - `import/no-unresolved`: Ensures imports point to valid modules
111
- - `import/order`: Enforces a consistent order of import statements
112
- - `import/no-duplicates`: Prevents duplicate imports
141
+ - `import-x/no-cycle`: Detects circular dependencies
142
+ - `import-x/no-self-import`: Prevents modules importing themselves
143
+ - `import-x/order`: Enforces a consistent order of import statements
144
+ - `import-x/consistent-type-specifier-style`: Consistent type import style
113
145
  - ...
114
146
 
115
147
  ### @stylistic/eslint-plugin
@@ -119,18 +151,17 @@ export default [
119
151
  - `@stylistic/quotes`: Enforces consistent quote style
120
152
  - ...
121
153
 
154
+ ### eslint-config-prettier
155
+
156
+ - Automatically disables all ESLint rules that conflict with Prettier
157
+ - Must be the last configuration in the array
158
+
122
159
  ## Contributing
123
160
 
124
161
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines.
162
+ See [CHANGELOG.md](./CHANGELOG.md) for version history.
163
+ See [RELEASE_NOTES.md](./RELEASE_NOTES.md) for detailed release notes.
125
164
 
126
165
  ## License
127
166
 
128
167
  MIT
129
-
130
- ```
131
-
132
- ```
133
-
134
- ```
135
-
136
- ```
package/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  jest: () => jest,
39
39
  json: () => json,
40
40
  playwright: () => playwright,
41
+ prettier: () => prettier,
41
42
  recommended: () => recommended,
42
43
  rxjs: () => rxjs,
43
44
  storybook: () => storybook,
@@ -74,11 +75,27 @@ var angularTemplate = (0, import_config.defineConfig)([
74
75
  "@angular-eslint/template/prefer-self-closing-tags": "error",
75
76
  "@angular-eslint/template/prefer-ngsrc": "error",
76
77
  "@angular-eslint/template/prefer-built-in-pipes": "error",
77
- "@angular-eslint/template/no-call-expression": "error",
78
78
  "@angular-eslint/template/no-interpolation-in-attributes": "error",
79
79
  "@angular-eslint/template/no-nested-tags": "error",
80
80
  "@angular-eslint/template/use-track-by-function": "error",
81
81
  "@angular-eslint/template/label-has-associated-control": "error",
82
+ "@angular-eslint/template/prefer-at-else": "error",
83
+ // v21: @if/@else syntax
84
+ "@angular-eslint/template/prefer-at-empty": "error",
85
+ // v21: @for/@empty syntax
86
+ "@angular-eslint/template/prefer-contextual-for-variables": "error",
87
+ // v21: Use $index, $first, etc.
88
+ "@angular-eslint/template/no-empty-control-flow": "error",
89
+ // v21: Catch incomplete refactoring
90
+ "@angular-eslint/template/prefer-class-binding": "error",
91
+ // v21.2.0: Prefer [class] over ngClass
92
+ "@angular-eslint/template/prefer-static-string-properties": "error",
93
+ // Reduce change detection
94
+ "@angular-eslint/template/conditional-complexity": [
95
+ "warn",
96
+ { maxComplexity: 5 }
97
+ ],
98
+ // Template readability
82
99
  "@angular-eslint/template/attributes-order": [
83
100
  "error",
84
101
  {
@@ -125,7 +142,17 @@ var angular = (0, import_config2.defineConfig)([
125
142
  "@angular-eslint/use-injectable-provided-in": "error",
126
143
  "@angular-eslint/prefer-signal-model": "error",
127
144
  "@angular-eslint/no-uncalled-signals": "error",
128
- "@angular-eslint/prefer-host-metadata-property": "error"
145
+ "@angular-eslint/prefer-host-metadata-property": "error",
146
+ "@angular-eslint/no-implicit-take-until-destroyed": "error",
147
+ // v21.2.0: Enforce explicit DestroyRef
148
+ "@angular-eslint/no-async-lifecycle-method": "error",
149
+ // Prevent async lifecycle hooks
150
+ "@angular-eslint/prefer-output-readonly": "warn",
151
+ // Prevent accidental reassignment
152
+ "@angular-eslint/prefer-output-emitter-ref": "warn",
153
+ // Modern output() function over @Output()
154
+ "@angular-eslint/sort-lifecycle-methods": "warn"
155
+ // Consistent lifecycle method ordering
129
156
  }
130
157
  }
131
158
  ]);
@@ -188,7 +215,10 @@ var base = (0, import_config3.defineConfig)([
188
215
  "no-useless-concat": "error",
189
216
  "prefer-template": "error",
190
217
  "no-nested-ternary": "error",
191
- "no-return-await": "error",
218
+ "no-useless-assignment": "error",
219
+ // NEW: Catch unused assignments (ESLint v9)
220
+ "no-object-constructor": "error",
221
+ // Disallow new Object() without arguments
192
222
  "no-else-return": ["error", { allowElseIf: false }],
193
223
  // Prefer early returns
194
224
  "no-multi-assign": "error",
@@ -370,9 +400,29 @@ var import_config5 = require("eslint/config");
370
400
  var jest = (0, import_config5.defineConfig)([
371
401
  {
372
402
  files: ["**/*.spec.ts", "**/*.spec.js"],
373
- extends: [import_eslint_plugin_jest.default.configs["flat/all"]],
403
+ extends: [import_eslint_plugin_jest.default.configs["flat/recommended"]],
374
404
  rules: {
375
- "jest/no-done-callback": "off"
405
+ "jest/no-done-callback": "off",
406
+ // Elevated from recommended defaults
407
+ "jest/no-focused-tests": "error",
408
+ "jest/no-identical-title": "error",
409
+ "jest/valid-expect": "error",
410
+ // Style rules (selective additions from flat/style)
411
+ "jest/prefer-to-be": "warn",
412
+ "jest/prefer-to-contain": "warn",
413
+ "jest/prefer-to-have-length": "warn",
414
+ // Best practice additions
415
+ "jest/consistent-test-it": ["error", { fn: "it" }],
416
+ "jest/prefer-comparison-matcher": "error",
417
+ "jest/prefer-equality-matcher": "error",
418
+ "jest/prefer-hooks-on-top": "error",
419
+ "jest/prefer-spy-on": "error",
420
+ "jest/prefer-strict-equal": "warn",
421
+ "jest/prefer-mock-promise-shorthand": "error",
422
+ "jest/no-conditional-in-test": "warn",
423
+ "jest/no-duplicate-hooks": "error",
424
+ "jest/no-test-return-statement": "error",
425
+ "jest/require-to-throw-message": "warn"
376
426
  }
377
427
  }
378
428
  ]);
@@ -384,13 +434,22 @@ var json = (0, import_config6.defineConfig)([
384
434
  {
385
435
  files: ["**/*.json"],
386
436
  extends: [import_eslint_plugin_json.default.configs.recommended]
437
+ },
438
+ {
439
+ files: ["**/tsconfig*.json", "**/.vscode/*.json"],
440
+ extends: [import_eslint_plugin_json.default.configs["recommended-with-comments"]]
387
441
  }
388
442
  ]);
389
443
 
444
+ // packages/lint-suite/src/lib/prettier.ts
445
+ var import_eslint_config_prettier = __toESM(require("eslint-config-prettier"), 1);
446
+ var import_config7 = require("eslint/config");
447
+ var prettier = (0, import_config7.defineConfig)([import_eslint_config_prettier.default]);
448
+
390
449
  // packages/lint-suite/src/lib/rxjs.ts
391
450
  var import_eslint_plugin_rxjs = __toESM(require("@smarttools/eslint-plugin-rxjs"), 1);
392
- var import_config7 = require("eslint/config");
393
- var rxjs = (0, import_config7.defineConfig)([
451
+ var import_config8 = require("eslint/config");
452
+ var rxjs = (0, import_config8.defineConfig)([
394
453
  {
395
454
  files: [
396
455
  "**/*.js",
@@ -446,7 +505,13 @@ var rxjs = (0, import_config7.defineConfig)([
446
505
  "rxjs/no-unsafe-first": "error",
447
506
  "rxjs/no-topromise": "error",
448
507
  "rxjs/throw-error": "error",
449
- "rxjs/no-async-subscribe": "off"
508
+ "rxjs/no-async-subscribe": "off",
509
+ "rxjs/no-exposed-subjects": "error",
510
+ // Enforce subject encapsulation
511
+ "rxjs/no-cyclic-action": "error",
512
+ // Prevent infinite loops in NgRx effects
513
+ "rxjs/no-subscribe-handlers": "warn"
514
+ // Prefer observer objects (complements prefer-observer)
450
515
  }
451
516
  },
452
517
  {
@@ -459,12 +524,18 @@ var rxjs = (0, import_config7.defineConfig)([
459
524
 
460
525
  // packages/lint-suite/src/lib/storybook.ts
461
526
  var import_eslint_plugin_storybook = __toESM(require("eslint-plugin-storybook"), 1);
462
- var import_config8 = require("eslint/config");
463
- var storybook = (0, import_config8.defineConfig)([
527
+ var import_config9 = require("eslint/config");
528
+ var storybook = (0, import_config9.defineConfig)([
464
529
  {
465
530
  ignores: ["!.storybook"],
466
531
  files: ["**/*.stories.ts"],
467
- extends: [import_eslint_plugin_storybook.default.configs["flat/recommended"]]
532
+ extends: [import_eslint_plugin_storybook.default.configs["flat/recommended"]],
533
+ rules: {
534
+ "storybook/csf-component": "warn",
535
+ // Ensure component property is set
536
+ "storybook/no-stories-of": "error"
537
+ // Prevent deprecated storiesOf API
538
+ }
468
539
  }
469
540
  ]);
470
541
 
@@ -472,7 +543,7 @@ var storybook = (0, import_config8.defineConfig)([
472
543
  var import_eslint_plugin4 = require("@nx/eslint-plugin");
473
544
  var import_eslint_import_resolver_typescript = require("eslint-import-resolver-typescript");
474
545
  var import_eslint_plugin_import_x2 = __toESM(require("eslint-plugin-import-x"), 1);
475
- var import_config9 = require("eslint/config");
546
+ var import_config10 = require("eslint/config");
476
547
 
477
548
  // packages/lint-suite/src/lib/rules/explicit-accessibility.ts
478
549
  var import_utils = require("@typescript-eslint/utils");
@@ -613,7 +684,7 @@ var localTypescriptRules = {
613
684
  "explicit-accessibility": explicit_accessibility_default
614
685
  }
615
686
  };
616
- var typescript = (0, import_config9.defineConfig)([
687
+ var typescript = (0, import_config10.defineConfig)([
617
688
  // NOTE: Checks for 'prettier' and 'eslint-plugin-prettier'
618
689
  {
619
690
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
@@ -639,6 +710,13 @@ var typescript = (0, import_config9.defineConfig)([
639
710
  "@typescript-eslint/no-unsafe-member-access": "error",
640
711
  "@typescript-eslint/no-unsafe-return": "error",
641
712
  "@typescript-eslint/no-unsafe-argument": "error",
713
+ // New v8 type-safety rules (replacements for deprecated ban-types)
714
+ "@typescript-eslint/no-empty-object-type": "error",
715
+ // Bans confusing {} type
716
+ "@typescript-eslint/no-unsafe-function-type": "error",
717
+ // Bans Function type
718
+ "@typescript-eslint/no-wrapper-object-types": "error",
719
+ // Bans Number, String, Boolean wrappers
642
720
  // B. Explicit Annotations (partially covered, explicit module boundaries is key)
643
721
  "@typescript-eslint/explicit-module-boundary-types": "error",
644
722
  // Consider enabling if maximum explicitness is desired, even if verbose:
@@ -659,8 +737,14 @@ var typescript = (0, import_config9.defineConfig)([
659
737
  // E. Best Practices & Potential Errors (Some covered by presets, fine-tuning here)
660
738
  "@typescript-eslint/no-inferrable-types": "warn",
661
739
  // Keep as warn or disable if inference is preferred
662
- "@typescript-eslint/no-var-requires": "error",
663
- // Use ES modules
740
+ "@typescript-eslint/no-require-imports": "error",
741
+ // Use ES modules (replaces deprecated no-var-requires)
742
+ "@typescript-eslint/only-throw-error": "error",
743
+ // Replaces deprecated no-throw-literal
744
+ "@typescript-eslint/no-array-delete": "error",
745
+ // Prevents delete arr[0]
746
+ "@typescript-eslint/return-await": ["error", "in-try-catch"],
747
+ // Better stack traces in try/catch
664
748
  "@typescript-eslint/require-await": "error",
665
749
  "@typescript-eslint/restrict-template-expressions": [
666
750
  "error",
@@ -680,6 +764,32 @@ var typescript = (0, import_config9.defineConfig)([
680
764
  "@typescript-eslint/no-useless-constructor": "error",
681
765
  "@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
682
766
  // Prefer dot notation where possible
767
+ // Strict type-checked rules
768
+ "@typescript-eslint/no-unnecessary-condition": "error",
769
+ // Catches always-true/false conditions
770
+ "@typescript-eslint/no-confusing-void-expression": "error",
771
+ // Prevent confusing void usage
772
+ "@typescript-eslint/no-dynamic-delete": "error",
773
+ // Use Map instead of delete obj[key]
774
+ "@typescript-eslint/no-extraneous-class": ["error", { allowWithDecorator: true }],
775
+ // Allow Angular classes
776
+ "@typescript-eslint/no-invalid-void-type": "error",
777
+ // Restrict void to return types
778
+ "@typescript-eslint/unified-signatures": "error",
779
+ // Merge overloads where possible
780
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
781
+ // Forbids foo! ?? bar
782
+ // Stylistic type-checked rules
783
+ "@typescript-eslint/prefer-find": "error",
784
+ // Use find() instead of filter()[0]
785
+ "@typescript-eslint/prefer-for-of": "error",
786
+ // Modern iteration
787
+ "@typescript-eslint/prefer-includes": "error",
788
+ // indexOf() → includes()
789
+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
790
+ // Modern string methods
791
+ "@typescript-eslint/prefer-regexp-exec": "error",
792
+ // Better regex performance
683
793
  // F. Consistency & Style (Some covered by stylistic-type-checked)
684
794
  "@typescript-eslint/consistent-type-assertions": [
685
795
  "error",
@@ -781,10 +891,18 @@ var typescript = (0, import_config9.defineConfig)([
781
891
  "import-x/no-unresolved": "error",
782
892
  "import-x/no-duplicates": "error",
783
893
  "import-x/prefer-default-export": "off",
784
- "import-x/no-useless-path-segments": "error",
894
+ "import-x/no-useless-path-segments": ["error", { noUselessIndex: true }],
785
895
  "import-x/newline-after-import": "error",
786
896
  "import-x/first": "error",
787
- "import-x/no-mutable-exports": "error"
897
+ "import-x/no-mutable-exports": "error",
898
+ "import-x/no-self-import": "error",
899
+ // Prevents modules importing themselves
900
+ "import-x/no-cycle": ["error", { maxDepth: 2, ignoreExternal: true }],
901
+ // Prevents circular dependencies
902
+ "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
903
+ // Consistent type imports
904
+ "import-x/no-anonymous-default-export": "error"
905
+ // Improve code searchability
788
906
  },
789
907
  settings: {
790
908
  "import-x/resolver-next": [
@@ -838,38 +956,63 @@ var typescript = (0, import_config9.defineConfig)([
838
956
 
839
957
  // packages/lint-suite/src/lib/vitest.ts
840
958
  var import_eslint_plugin5 = __toESM(require("@vitest/eslint-plugin"), 1);
841
- var import_config10 = require("eslint/config");
959
+ var import_config11 = require("eslint/config");
842
960
  var vitestPlugin = {
843
961
  vitest: import_eslint_plugin5.default
844
962
  };
845
- var vitest = (0, import_config10.defineConfig)([
963
+ var vitest = (0, import_config11.defineConfig)([
846
964
  {
847
965
  files: ["**/*.spec.ts", "**/*.spec.js", "**/*.test.ts", "**/*.test.js"],
848
966
  plugins: vitestPlugin,
849
967
  rules: {
850
968
  ...import_eslint_plugin5.default.configs.recommended.rules,
851
- "vitest/max-nested-describe": ["error", { max: 3 }]
969
+ "vitest/max-nested-describe": ["error", { max: 3 }],
970
+ // Matcher improvements
971
+ "vitest/prefer-to-be": "error",
972
+ "vitest/prefer-to-contain": "error",
973
+ "vitest/prefer-to-have-length": "error",
974
+ "vitest/prefer-comparison-matcher": "error",
975
+ "vitest/prefer-strict-equal": "warn",
976
+ // Test quality
977
+ "vitest/prefer-spy-on": "error",
978
+ "vitest/prefer-hooks-on-top": "error",
979
+ "vitest/prefer-each": "warn",
980
+ "vitest/no-conditional-in-test": "error",
981
+ "vitest/no-test-return-statement": "error",
982
+ "vitest/prefer-mock-promise-shorthand": "error"
852
983
  }
853
984
  }
854
985
  ]);
855
986
 
856
987
  // packages/lint-suite/src/lib/playwright.ts
857
988
  var import_eslint_plugin_playwright = __toESM(require("eslint-plugin-playwright"), 1);
858
- var import_config11 = require("eslint/config");
859
- var playwright = (0, import_config11.defineConfig)([
989
+ var import_config12 = require("eslint/config");
990
+ var playwright = (0, import_config12.defineConfig)([
860
991
  {
861
992
  ...import_eslint_plugin_playwright.default.configs["flat/recommended"],
862
993
  files: ["**/*.e2e.ts", "**/*.e2e.js"],
863
994
  rules: {
864
- ...import_eslint_plugin_playwright.default.configs["flat/recommended"].rules
995
+ ...import_eslint_plugin_playwright.default.configs["flat/recommended"].rules,
996
+ // Locator best practices
997
+ "playwright/prefer-locator": "error",
998
+ "playwright/prefer-native-locators": "error",
999
+ "playwright/no-nth-methods": "warn",
1000
+ // Matcher improvements
1001
+ "playwright/prefer-to-be": "error",
1002
+ "playwright/prefer-to-have-count": "error",
1003
+ "playwright/prefer-to-have-length": "error",
1004
+ "playwright/prefer-comparison-matcher": "error",
1005
+ // Test structure
1006
+ "playwright/prefer-hooks-on-top": "error",
1007
+ "playwright/require-top-level-describe": "warn"
865
1008
  }
866
1009
  }
867
1010
  ]);
868
1011
 
869
1012
  // packages/lint-suite/src/lib/boundaries.ts
870
1013
  var import_eslint_plugin_boundaries = __toESM(require("eslint-plugin-boundaries"), 1);
871
- var import_config12 = require("eslint/config");
872
- var boundaries = (0, import_config12.defineConfig)([
1014
+ var import_config13 = require("eslint/config");
1015
+ var boundaries = (0, import_config13.defineConfig)([
873
1016
  {
874
1017
  files: [
875
1018
  "**/*.js",
@@ -917,6 +1060,8 @@ var boundaries = (0, import_config12.defineConfig)([
917
1060
  },
918
1061
  rules: {
919
1062
  ...import_eslint_plugin_boundaries.default.configs.recommended.rules,
1063
+ "boundaries/no-unknown": "warn",
1064
+ // Warn on imports from uncategorized files
920
1065
  "boundaries/element-types": [
921
1066
  "error",
922
1067
  {
@@ -973,7 +1118,9 @@ var recommended = [
973
1118
  ...boundaries,
974
1119
  ...storybook,
975
1120
  ...vitest,
976
- ...playwright
1121
+ ...playwright,
1122
+ ...prettier
1123
+ // Must be last to disable formatting rules that conflict with Prettier
977
1124
  ];
978
1125
  // Annotate the CommonJS export names for ESM import in node:
979
1126
  0 && (module.exports = {
@@ -985,6 +1132,7 @@ var recommended = [
985
1132
  jest,
986
1133
  json,
987
1134
  playwright,
1135
+ prettier,
988
1136
  recommended,
989
1137
  rxjs,
990
1138
  storybook,