lint-suite 1.2.3 → 1.3.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.
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,6 +151,11 @@ 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.
@@ -128,11 +165,3 @@ See [RELEASE_NOTES.md](./RELEASE_NOTES.md) for detailed release notes.
128
165
  ## License
129
166
 
130
167
  MIT
131
-
132
- ```
133
-
134
- ```
135
-
136
- ```
137
-
138
- ```
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,
@@ -78,6 +79,23 @@ var angularTemplate = (0, import_config.defineConfig)([
78
79
  "@angular-eslint/template/no-nested-tags": "error",
79
80
  "@angular-eslint/template/use-track-by-function": "error",
80
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
81
99
  "@angular-eslint/template/attributes-order": [
82
100
  "error",
83
101
  {
@@ -124,7 +142,17 @@ var angular = (0, import_config2.defineConfig)([
124
142
  "@angular-eslint/use-injectable-provided-in": "error",
125
143
  "@angular-eslint/prefer-signal-model": "error",
126
144
  "@angular-eslint/no-uncalled-signals": "error",
127
- "@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
128
156
  }
129
157
  }
130
158
  ]);
@@ -187,7 +215,10 @@ var base = (0, import_config3.defineConfig)([
187
215
  "no-useless-concat": "error",
188
216
  "prefer-template": "error",
189
217
  "no-nested-ternary": "error",
190
- "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
191
222
  "no-else-return": ["error", { allowElseIf: false }],
192
223
  // Prefer early returns
193
224
  "no-multi-assign": "error",
@@ -369,9 +400,29 @@ var import_config5 = require("eslint/config");
369
400
  var jest = (0, import_config5.defineConfig)([
370
401
  {
371
402
  files: ["**/*.spec.ts", "**/*.spec.js"],
372
- extends: [import_eslint_plugin_jest.default.configs["flat/all"]],
403
+ extends: [import_eslint_plugin_jest.default.configs["flat/recommended"]],
373
404
  rules: {
374
- "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"
375
426
  }
376
427
  }
377
428
  ]);
@@ -383,13 +434,22 @@ var json = (0, import_config6.defineConfig)([
383
434
  {
384
435
  files: ["**/*.json"],
385
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"]]
386
441
  }
387
442
  ]);
388
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
+
389
449
  // packages/lint-suite/src/lib/rxjs.ts
390
450
  var import_eslint_plugin_rxjs = __toESM(require("@smarttools/eslint-plugin-rxjs"), 1);
391
- var import_config7 = require("eslint/config");
392
- var rxjs = (0, import_config7.defineConfig)([
451
+ var import_config8 = require("eslint/config");
452
+ var rxjs = (0, import_config8.defineConfig)([
393
453
  {
394
454
  files: [
395
455
  "**/*.js",
@@ -445,25 +505,38 @@ var rxjs = (0, import_config7.defineConfig)([
445
505
  "rxjs/no-unsafe-first": "error",
446
506
  "rxjs/no-topromise": "error",
447
507
  "rxjs/throw-error": "error",
448
- "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)
449
515
  }
450
516
  },
451
517
  {
452
518
  files: ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.js", "**/*.test.js"],
453
519
  rules: {
454
- "rxjs/no-ignored-subscription": "off"
520
+ "rxjs/no-ignored-subscription": "off",
521
+ "rxjs/no-subscribe-handlers": "off"
455
522
  }
456
523
  }
457
524
  ]);
458
525
 
459
526
  // packages/lint-suite/src/lib/storybook.ts
460
527
  var import_eslint_plugin_storybook = __toESM(require("eslint-plugin-storybook"), 1);
461
- var import_config8 = require("eslint/config");
462
- var storybook = (0, import_config8.defineConfig)([
528
+ var import_config9 = require("eslint/config");
529
+ var storybook = (0, import_config9.defineConfig)([
463
530
  {
464
531
  ignores: ["!.storybook"],
465
532
  files: ["**/*.stories.ts"],
466
- extends: [import_eslint_plugin_storybook.default.configs["flat/recommended"]]
533
+ extends: [import_eslint_plugin_storybook.default.configs["flat/recommended"]],
534
+ rules: {
535
+ "storybook/csf-component": "warn",
536
+ // Ensure component property is set
537
+ "storybook/no-stories-of": "error"
538
+ // Prevent deprecated storiesOf API
539
+ }
467
540
  }
468
541
  ]);
469
542
 
@@ -471,7 +544,7 @@ var storybook = (0, import_config8.defineConfig)([
471
544
  var import_eslint_plugin4 = require("@nx/eslint-plugin");
472
545
  var import_eslint_import_resolver_typescript = require("eslint-import-resolver-typescript");
473
546
  var import_eslint_plugin_import_x2 = __toESM(require("eslint-plugin-import-x"), 1);
474
- var import_config9 = require("eslint/config");
547
+ var import_config10 = require("eslint/config");
475
548
 
476
549
  // packages/lint-suite/src/lib/rules/explicit-accessibility.ts
477
550
  var import_utils = require("@typescript-eslint/utils");
@@ -612,7 +685,7 @@ var localTypescriptRules = {
612
685
  "explicit-accessibility": explicit_accessibility_default
613
686
  }
614
687
  };
615
- var typescript = (0, import_config9.defineConfig)([
688
+ var typescript = (0, import_config10.defineConfig)([
616
689
  // NOTE: Checks for 'prettier' and 'eslint-plugin-prettier'
617
690
  {
618
691
  files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
@@ -638,6 +711,13 @@ var typescript = (0, import_config9.defineConfig)([
638
711
  "@typescript-eslint/no-unsafe-member-access": "error",
639
712
  "@typescript-eslint/no-unsafe-return": "error",
640
713
  "@typescript-eslint/no-unsafe-argument": "error",
714
+ // New v8 type-safety rules (replacements for deprecated ban-types)
715
+ "@typescript-eslint/no-empty-object-type": "error",
716
+ // Bans confusing {} type
717
+ "@typescript-eslint/no-unsafe-function-type": "error",
718
+ // Bans Function type
719
+ "@typescript-eslint/no-wrapper-object-types": "error",
720
+ // Bans Number, String, Boolean wrappers
641
721
  // B. Explicit Annotations (partially covered, explicit module boundaries is key)
642
722
  "@typescript-eslint/explicit-module-boundary-types": "error",
643
723
  // Consider enabling if maximum explicitness is desired, even if verbose:
@@ -658,8 +738,14 @@ var typescript = (0, import_config9.defineConfig)([
658
738
  // E. Best Practices & Potential Errors (Some covered by presets, fine-tuning here)
659
739
  "@typescript-eslint/no-inferrable-types": "warn",
660
740
  // Keep as warn or disable if inference is preferred
661
- "@typescript-eslint/no-var-requires": "error",
662
- // Use ES modules
741
+ "@typescript-eslint/no-require-imports": "error",
742
+ // Use ES modules (replaces deprecated no-var-requires)
743
+ "@typescript-eslint/only-throw-error": "error",
744
+ // Replaces deprecated no-throw-literal
745
+ "@typescript-eslint/no-array-delete": "error",
746
+ // Prevents delete arr[0]
747
+ "@typescript-eslint/return-await": ["error", "in-try-catch"],
748
+ // Better stack traces in try/catch
663
749
  "@typescript-eslint/require-await": "error",
664
750
  "@typescript-eslint/restrict-template-expressions": [
665
751
  "error",
@@ -679,6 +765,32 @@ var typescript = (0, import_config9.defineConfig)([
679
765
  "@typescript-eslint/no-useless-constructor": "error",
680
766
  "@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
681
767
  // Prefer dot notation where possible
768
+ // Strict type-checked rules
769
+ "@typescript-eslint/no-unnecessary-condition": "error",
770
+ // Catches always-true/false conditions
771
+ "@typescript-eslint/no-confusing-void-expression": "error",
772
+ // Prevent confusing void usage
773
+ "@typescript-eslint/no-dynamic-delete": "error",
774
+ // Use Map instead of delete obj[key]
775
+ "@typescript-eslint/no-extraneous-class": ["error", { allowWithDecorator: true }],
776
+ // Allow Angular classes
777
+ "@typescript-eslint/no-invalid-void-type": "error",
778
+ // Restrict void to return types
779
+ "@typescript-eslint/unified-signatures": "error",
780
+ // Merge overloads where possible
781
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
782
+ // Forbids foo! ?? bar
783
+ // Stylistic type-checked rules
784
+ "@typescript-eslint/prefer-find": "error",
785
+ // Use find() instead of filter()[0]
786
+ "@typescript-eslint/prefer-for-of": "error",
787
+ // Modern iteration
788
+ "@typescript-eslint/prefer-includes": "error",
789
+ // indexOf() → includes()
790
+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
791
+ // Modern string methods
792
+ "@typescript-eslint/prefer-regexp-exec": "error",
793
+ // Better regex performance
682
794
  // F. Consistency & Style (Some covered by stylistic-type-checked)
683
795
  "@typescript-eslint/consistent-type-assertions": [
684
796
  "error",
@@ -780,10 +892,18 @@ var typescript = (0, import_config9.defineConfig)([
780
892
  "import-x/no-unresolved": "error",
781
893
  "import-x/no-duplicates": "error",
782
894
  "import-x/prefer-default-export": "off",
783
- "import-x/no-useless-path-segments": "error",
895
+ "import-x/no-useless-path-segments": ["error", { noUselessIndex: true }],
784
896
  "import-x/newline-after-import": "error",
785
897
  "import-x/first": "error",
786
- "import-x/no-mutable-exports": "error"
898
+ "import-x/no-mutable-exports": "error",
899
+ "import-x/no-self-import": "error",
900
+ // Prevents modules importing themselves
901
+ "import-x/no-cycle": ["error", { maxDepth: 2, ignoreExternal: true }],
902
+ // Prevents circular dependencies
903
+ "import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
904
+ // Consistent type imports
905
+ "import-x/no-anonymous-default-export": "error"
906
+ // Improve code searchability
787
907
  },
788
908
  settings: {
789
909
  "import-x/resolver-next": [
@@ -808,7 +928,8 @@ var typescript = (0, import_config9.defineConfig)([
808
928
  {
809
929
  files: ["**/*.action.ts"],
810
930
  rules: {
811
- "@typescript-eslint/no-namespace": "off"
931
+ "@typescript-eslint/no-namespace": "off",
932
+ "@typescript-eslint/no-extraneous-class": "off"
812
933
  }
813
934
  },
814
935
  {
@@ -837,38 +958,62 @@ var typescript = (0, import_config9.defineConfig)([
837
958
 
838
959
  // packages/lint-suite/src/lib/vitest.ts
839
960
  var import_eslint_plugin5 = __toESM(require("@vitest/eslint-plugin"), 1);
840
- var import_config10 = require("eslint/config");
961
+ var import_config11 = require("eslint/config");
841
962
  var vitestPlugin = {
842
963
  vitest: import_eslint_plugin5.default
843
964
  };
844
- var vitest = (0, import_config10.defineConfig)([
965
+ var vitest = (0, import_config11.defineConfig)([
845
966
  {
846
967
  files: ["**/*.spec.ts", "**/*.spec.js", "**/*.test.ts", "**/*.test.js"],
847
968
  plugins: vitestPlugin,
848
969
  rules: {
849
970
  ...import_eslint_plugin5.default.configs.recommended.rules,
850
- "vitest/max-nested-describe": ["error", { max: 3 }]
971
+ "vitest/max-nested-describe": ["error", { max: 3 }],
972
+ // Matcher improvements
973
+ "vitest/prefer-to-be": "error",
974
+ "vitest/prefer-to-contain": "error",
975
+ "vitest/prefer-to-have-length": "error",
976
+ "vitest/prefer-comparison-matcher": "error",
977
+ "vitest/prefer-strict-equal": "warn",
978
+ // Test quality
979
+ "vitest/prefer-spy-on": "error",
980
+ "vitest/prefer-hooks-on-top": "error",
981
+ "vitest/prefer-each": "warn",
982
+ "vitest/no-test-return-statement": "error",
983
+ "vitest/prefer-mock-promise-shorthand": "error"
851
984
  }
852
985
  }
853
986
  ]);
854
987
 
855
988
  // packages/lint-suite/src/lib/playwright.ts
856
989
  var import_eslint_plugin_playwright = __toESM(require("eslint-plugin-playwright"), 1);
857
- var import_config11 = require("eslint/config");
858
- var playwright = (0, import_config11.defineConfig)([
990
+ var import_config12 = require("eslint/config");
991
+ var playwright = (0, import_config12.defineConfig)([
859
992
  {
860
993
  ...import_eslint_plugin_playwright.default.configs["flat/recommended"],
861
994
  files: ["**/*.e2e.ts", "**/*.e2e.js"],
862
995
  rules: {
863
- ...import_eslint_plugin_playwright.default.configs["flat/recommended"].rules
996
+ ...import_eslint_plugin_playwright.default.configs["flat/recommended"].rules,
997
+ // Locator best practices
998
+ "playwright/prefer-locator": "error",
999
+ "playwright/prefer-native-locators": "error",
1000
+ "playwright/no-nth-methods": "warn",
1001
+ // Matcher improvements
1002
+ "playwright/prefer-to-be": "error",
1003
+ "playwright/prefer-to-have-count": "error",
1004
+ "playwright/prefer-to-have-length": "error",
1005
+ "playwright/prefer-comparison-matcher": "error",
1006
+ // Test structure
1007
+ "playwright/prefer-hooks-on-top": "error",
1008
+ "playwright/require-top-level-describe": "warn"
864
1009
  }
865
1010
  }
866
1011
  ]);
867
1012
 
868
1013
  // packages/lint-suite/src/lib/boundaries.ts
869
1014
  var import_eslint_plugin_boundaries = __toESM(require("eslint-plugin-boundaries"), 1);
870
- var import_config12 = require("eslint/config");
871
- var boundaries = (0, import_config12.defineConfig)([
1015
+ var import_config13 = require("eslint/config");
1016
+ var boundaries = (0, import_config13.defineConfig)([
872
1017
  {
873
1018
  files: [
874
1019
  "**/*.js",
@@ -972,7 +1117,9 @@ var recommended = [
972
1117
  ...boundaries,
973
1118
  ...storybook,
974
1119
  ...vitest,
975
- ...playwright
1120
+ ...playwright,
1121
+ ...prettier
1122
+ // Must be last to disable formatting rules that conflict with Prettier
976
1123
  ];
977
1124
  // Annotate the CommonJS export names for ESM import in node:
978
1125
  0 && (module.exports = {
@@ -984,6 +1131,7 @@ var recommended = [
984
1131
  jest,
985
1132
  json,
986
1133
  playwright,
1134
+ prettier,
987
1135
  recommended,
988
1136
  rxjs,
989
1137
  storybook,