@prover-coder-ai/eslint-plugin-suggest-members 0.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/.jscpd.json +16 -0
- package/README.md +104 -0
- package/biome.json +37 -0
- package/docs/rules/no-loop-over-enums.md +29 -0
- package/docs/rules/suggest-exports.md +15 -0
- package/docs/rules/suggest-imports.md +15 -0
- package/docs/rules/suggest-members.md +15 -0
- package/docs/rules/suggest-missing-names.md +13 -0
- package/docs/rules/suggest-module-paths.md +15 -0
- package/eslint.config.mts +265 -0
- package/eslint.effect-ts-check.config.mjs +220 -0
- package/linter.config.json +32 -0
- package/package.json +79 -0
- package/scripts/checkFunctionalCore.ts +488 -0
- package/src/core/axioms.ts +23 -0
- package/src/core/effects/index.ts +39 -0
- package/src/core/formatting/messages.ts +315 -0
- package/src/core/index.ts +71 -0
- package/src/core/plugin-meta.ts +12 -0
- package/src/core/similarity/composite.ts +69 -0
- package/src/core/similarity/helpers.ts +34 -0
- package/src/core/similarity/index.ts +10 -0
- package/src/core/similarity/jaro-winkler.ts +25 -0
- package/src/core/similarity/jaro.ts +99 -0
- package/src/core/suggestion/engine.ts +35 -0
- package/src/core/types/domain.ts +28 -0
- package/src/core/types/eslint-nodes.ts +62 -0
- package/src/core/types/validation.ts +185 -0
- package/src/core/validation/candidates.ts +29 -0
- package/src/core/validation/module-path-utils.ts +33 -0
- package/src/core/validation/node-builtin-exports.ts +46 -0
- package/src/core/validators/index.ts +14 -0
- package/src/core/validators/node-predicates.ts +92 -0
- package/src/index.ts +56 -0
- package/src/rules/index.ts +25 -0
- package/src/rules/suggest-exports/index.ts +121 -0
- package/src/rules/suggest-imports/index.ts +25 -0
- package/src/rules/suggest-members/index.ts +154 -0
- package/src/rules/suggest-missing-names/index.ts +116 -0
- package/src/rules/suggest-module-paths/index.ts +101 -0
- package/src/shell/effects/errors.ts +80 -0
- package/src/shell/services/filesystem.ts +136 -0
- package/src/shell/services/typescript-compiler-effects.ts +85 -0
- package/src/shell/services/typescript-compiler-helpers.ts +89 -0
- package/src/shell/services/typescript-compiler-module-effects.ts +296 -0
- package/src/shell/services/typescript-compiler.ts +112 -0
- package/src/shell/services/typescript-effect-utils.ts +123 -0
- package/src/shell/shared/effect-utils.ts +18 -0
- package/src/shell/shared/import-validation-base.ts +181 -0
- package/src/shell/shared/import-validation-rule-factory.ts +116 -0
- package/src/shell/shared/validation-helpers.ts +94 -0
- package/src/shell/shared/validation-runner.ts +45 -0
- package/src/shell/validation/export-validation-effect.ts +54 -0
- package/src/shell/validation/import-validation-effect.ts +49 -0
- package/src/shell/validation/local-export-validation-effect.ts +10 -0
- package/src/shell/validation/member-validation-effect.ts +307 -0
- package/src/shell/validation/missing-name-validation-base.ts +153 -0
- package/src/shell/validation/missing-name-validation-effect.ts +10 -0
- package/src/shell/validation/missing-name-validators.ts +52 -0
- package/src/shell/validation/module-path-index.ts +144 -0
- package/src/shell/validation/module-validation-effect.ts +220 -0
- package/src/shell/validation/suggestion-signatures.ts +63 -0
- package/src/shell/validation/validation-base-effect.ts +165 -0
- package/tests/core/message-formatting.test.ts +121 -0
- package/tests/core/suggestion-engine.test.ts +34 -0
- package/tests/fixtures/consumer.ts +1 -0
- package/tests/fixtures/module-paths/alpha.ts +1 -0
- package/tests/fixtures/module-paths/beta.ts +1 -0
- package/tests/fixtures/modules/exports.ts +9 -0
- package/tests/plugin-signature.test.ts +69 -0
- package/tests/rules/suggest-imports-exports.test.ts +91 -0
- package/tests/rules/suggest-members.test.ts +98 -0
- package/tests/rules/suggest-missing-names.test.ts +35 -0
- package/tests/rules/suggest-module-paths.test.ts +54 -0
- package/tests/utils/rule-tester.ts +41 -0
- package/tsconfig.build.json +13 -0
- package/tsconfig.json +22 -0
- package/types/eslint-plugins.d.ts +15 -0
- package/vite.config.ts +33 -0
- package/vitest.config.ts +87 -0
package/.jscpd.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"threshold": 0,
|
|
3
|
+
"minTokens": 30,
|
|
4
|
+
"minLines": 5,
|
|
5
|
+
"ignore": [
|
|
6
|
+
"**/node_modules/**",
|
|
7
|
+
"**/build/**",
|
|
8
|
+
"**/dist/**",
|
|
9
|
+
"**/*.min.js",
|
|
10
|
+
"**/reports/**"
|
|
11
|
+
],
|
|
12
|
+
"skipComments": true,
|
|
13
|
+
"ignorePattern": [
|
|
14
|
+
"private readonly \\w+: \\w+;\\s*private readonly \\w+: \\w+;\\s*private \\w+: \\w+ \\| null = null;\\s*private \\w+: \\w+ \\| null = null;"
|
|
15
|
+
]
|
|
16
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# `@prover-coder-ai/eslint-plugin-suggest-members`
|
|
2
|
+
|
|
3
|
+
Production‑ready ESLint plugin that suggests corrections for typos in TypeScript/JavaScript code. Built with Functional Core / Imperative Shell and Effect‑TS.
|
|
4
|
+
|
|
5
|
+
## ✨ Key Features
|
|
6
|
+
|
|
7
|
+
- Smart suggestions for typos (similarity scoring)
|
|
8
|
+
- TypeScript‑aware diagnostics with signatures
|
|
9
|
+
- Filesystem‑based module path suggestions
|
|
10
|
+
- Fully typed, Effect‑TS based architecture
|
|
11
|
+
|
|
12
|
+
## ⚙️ Configuration (ESLint v9+ Flat Config)
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
// eslint.config.js
|
|
16
|
+
import suggestMembers from "@prover-coder-ai/eslint-plugin-suggest-members"
|
|
17
|
+
|
|
18
|
+
export default [
|
|
19
|
+
{
|
|
20
|
+
files: ["**/*.{ts,tsx,js,jsx}"],
|
|
21
|
+
plugins: {
|
|
22
|
+
"suggest-members": suggestMembers
|
|
23
|
+
},
|
|
24
|
+
rules: {
|
|
25
|
+
"suggest-members/suggest-exports": "error",
|
|
26
|
+
"suggest-members/suggest-imports": "error",
|
|
27
|
+
"suggest-members/suggest-members": "error",
|
|
28
|
+
"suggest-members/suggest-missing-names": "error",
|
|
29
|
+
"suggest-members/suggest-module-paths": "error"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## ✅ Example Diagnostics
|
|
36
|
+
|
|
37
|
+
### Export Suggestions (`suggest-exports`)
|
|
38
|
+
```ts
|
|
39
|
+
// ❌ Typo in React hook import
|
|
40
|
+
export { useStae, useEffect } from "react"
|
|
41
|
+
// ✅ ESLint Error: Export 'useStae' does not exist on type 'typeof import("react")'. Did you mean:
|
|
42
|
+
// - useState
|
|
43
|
+
// - useRef
|
|
44
|
+
// - useMemo
|
|
45
|
+
// - useCallback
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Member Suggestions (`suggest-members`)
|
|
49
|
+
```ts
|
|
50
|
+
// ❌ Typo in localStorage method
|
|
51
|
+
localStorage.get1Item("token")
|
|
52
|
+
// ✅ ESLint Error: Property 'get1Item' does not exist on type 'Storage'. Did you mean:
|
|
53
|
+
// - getItem(key: string): string | null
|
|
54
|
+
// - setItem(key: string, value: string)
|
|
55
|
+
// - removeItem(key: string)
|
|
56
|
+
// - clear(): void
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Module Path Suggestions (`suggest-module-paths`)
|
|
60
|
+
```ts
|
|
61
|
+
// ❌ Typo in file path
|
|
62
|
+
import styles from "./HamsterKo1mbatPage.css"
|
|
63
|
+
// ✅ ESLint Error: Cannot find module "./HamsterKo1mbatPage.css". Did you mean:
|
|
64
|
+
// - ./HamsterKombatPage.css
|
|
65
|
+
// - ./HamsterKombatPage.tsx
|
|
66
|
+
// - ./HamsterKombatPage
|
|
67
|
+
// - ../ThemeParamsPage.css
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Import Suggestions (`suggest-imports`)
|
|
71
|
+
```ts
|
|
72
|
+
// ❌ Typo in named import
|
|
73
|
+
import { saveRe1f } from "./hooks"
|
|
74
|
+
// ✅ ESLint Error: Export 'saveRe1f' does not exist on type 'typeof import("./hooks")'. Did you mean:
|
|
75
|
+
// - saveRef
|
|
76
|
+
// - saveState
|
|
77
|
+
// - useRef
|
|
78
|
+
// - useState
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Missing Name Suggestions (`suggest-missing-names`)
|
|
82
|
+
```ts
|
|
83
|
+
// ❌ Typo in local identifier
|
|
84
|
+
const formatGree1ting = () => "ok"
|
|
85
|
+
formatGreeting()
|
|
86
|
+
// ✅ ESLint Error: Cannot find name 'formatGreeting'. Did you mean:
|
|
87
|
+
// - formatGree1ting(): string
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 📚 Rules
|
|
91
|
+
|
|
92
|
+
| Name | Description | TS Required |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| [suggest-exports](docs/rules/suggest-exports.md) | Suggests corrections for missing exports | ✅ |
|
|
95
|
+
| [suggest-imports](docs/rules/suggest-imports.md) | Suggests corrections for missing imports | ✅ |
|
|
96
|
+
| [suggest-members](docs/rules/suggest-members.md) | Suggests corrections for missing members | ✅ |
|
|
97
|
+
| [suggest-missing-names](docs/rules/suggest-missing-names.md) | Suggests corrections for unresolved identifiers | ✅ |
|
|
98
|
+
| [suggest-module-paths](docs/rules/suggest-module-paths.md) | Suggests corrections for missing module paths | ❌ |
|
|
99
|
+
|
|
100
|
+
## Development
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
pnpm --filter @prover-coder-ai/eslint-plugin-suggest-members test
|
|
104
|
+
```
|
package/biome.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"vcs": {
|
|
3
|
+
"enabled": false,
|
|
4
|
+
"clientKind": "git",
|
|
5
|
+
"useIgnoreFile": false
|
|
6
|
+
},
|
|
7
|
+
"files": {
|
|
8
|
+
"ignoreUnknown": false
|
|
9
|
+
},
|
|
10
|
+
"formatter": {
|
|
11
|
+
"enabled": false,
|
|
12
|
+
"indentStyle": "tab"
|
|
13
|
+
},
|
|
14
|
+
"assist": {
|
|
15
|
+
"actions": {
|
|
16
|
+
"source": {
|
|
17
|
+
"organizeImports": "off"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"linter": {
|
|
22
|
+
"enabled": false,
|
|
23
|
+
"rules": {
|
|
24
|
+
"recommended": false,
|
|
25
|
+
"suspicious": {
|
|
26
|
+
"noExplicitAny": "off"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"javascript": {
|
|
31
|
+
"formatter": {
|
|
32
|
+
"enabled": false,
|
|
33
|
+
"quoteStyle": "double",
|
|
34
|
+
"semicolons": "asNeeded"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Avoid looping over enums (`example-typed-linting/no-loop-over-enums`)
|
|
2
|
+
|
|
3
|
+
💭 This rule requires [type information](https://typescript-eslint.io/linting/typed-linting).
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
Example rule that demonstrates banning `for-in` looping over `enum`s.
|
|
8
|
+
|
|
9
|
+
## Valid
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const values = {};
|
|
13
|
+
for (const a in values) {
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const values = [];
|
|
19
|
+
for (const a of values) {
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Invalid
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
enum Values {}
|
|
27
|
+
for (const a of values) {
|
|
28
|
+
}
|
|
29
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Suggest export names (`suggest-exports`)
|
|
2
|
+
|
|
3
|
+
Reports when a re-export targets a missing export and suggests the closest matches.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// ❌ Typo in React hook import
|
|
9
|
+
export { useStae, useEffect } from "react"
|
|
10
|
+
// ✅ ESLint Error: Export 'useStae' does not exist on type 'typeof import("react")'. Did you mean:
|
|
11
|
+
// - useState
|
|
12
|
+
// - useRef
|
|
13
|
+
// - useMemo
|
|
14
|
+
// - useCallback
|
|
15
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Suggest import names (`suggest-imports`)
|
|
2
|
+
|
|
3
|
+
Reports when a named import does not exist and suggests similar exports.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// ❌ Typo in named import
|
|
9
|
+
import { saveRe1f } from "./hooks"
|
|
10
|
+
// ✅ ESLint Error: Export 'saveRe1f' does not exist on type 'typeof import("./hooks")'. Did you mean:
|
|
11
|
+
// - saveRef
|
|
12
|
+
// - saveState
|
|
13
|
+
// - useRef
|
|
14
|
+
// - useState
|
|
15
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Suggest member names (`suggest-members`)
|
|
2
|
+
|
|
3
|
+
Reports when accessing a missing member and suggests similar properties/methods with signatures.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// ❌ Typo in localStorage method
|
|
9
|
+
localStorage.get1Item("token")
|
|
10
|
+
// ✅ ESLint Error: Property 'get1Item' does not exist on type 'Storage'. Did you mean:
|
|
11
|
+
// - getItem(key: string): string | null
|
|
12
|
+
// - setItem(key: string, value: string)
|
|
13
|
+
// - removeItem(key: string)
|
|
14
|
+
// - clear(): void
|
|
15
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Suggest missing names (`suggest-missing-names`)
|
|
2
|
+
|
|
3
|
+
Reports unresolved identifiers and suggests similar in-scope names with signatures.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// ❌ Typo in local identifier
|
|
9
|
+
const formatGree1ting = () => "ok"
|
|
10
|
+
formatGreeting()
|
|
11
|
+
// ✅ ESLint Error: Cannot find name 'formatGreeting'. Did you mean:
|
|
12
|
+
// - formatGree1ting(): string
|
|
13
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Suggest module paths (`suggest-module-paths`)
|
|
2
|
+
|
|
3
|
+
Reports when a module path cannot be resolved and suggests similar paths.
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// ❌ Typo in file path
|
|
9
|
+
import styles from "./HamsterKo1mbatPage.css"
|
|
10
|
+
// ✅ ESLint Error: Cannot find module "./HamsterKo1mbatPage.css". Did you mean:
|
|
11
|
+
// - ./HamsterKombatPage.css
|
|
12
|
+
// - ./HamsterKombatPage.tsx
|
|
13
|
+
// - ./HamsterKombatPage
|
|
14
|
+
// - ../ThemeParamsPage.css
|
|
15
|
+
```
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// eslint.config.mjs
|
|
2
|
+
// @ts-check
|
|
3
|
+
import eslint from '@eslint/js';
|
|
4
|
+
import eslintPlugin from "eslint-plugin-eslint-plugin";
|
|
5
|
+
import tseslint from 'typescript-eslint';
|
|
6
|
+
import vitest from "eslint-plugin-vitest";
|
|
7
|
+
import suggestMembers from "@ton-ai-core/eslint-plugin-suggest-members";
|
|
8
|
+
import sonarjs from "eslint-plugin-sonarjs";
|
|
9
|
+
import unicorn from "eslint-plugin-unicorn";
|
|
10
|
+
import * as effectEslint from "@effect/eslint-plugin";
|
|
11
|
+
import { fixupPluginRules } from "@eslint/compat";
|
|
12
|
+
import codegen from "eslint-plugin-codegen";
|
|
13
|
+
import importPlugin from "eslint-plugin-import";
|
|
14
|
+
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
|
15
|
+
import sortDestructureKeys from "eslint-plugin-sort-destructure-keys";
|
|
16
|
+
import globals from "globals";
|
|
17
|
+
import eslintCommentsConfigs from "@eslint-community/eslint-plugin-eslint-comments/configs";
|
|
18
|
+
|
|
19
|
+
const codegenPlugin = fixupPluginRules(
|
|
20
|
+
codegen as unknown as Parameters<typeof fixupPluginRules>[0],
|
|
21
|
+
);
|
|
22
|
+
const suggestMembersRecommended = suggestMembers.configs["recommended"];
|
|
23
|
+
|
|
24
|
+
export default tseslint.config(
|
|
25
|
+
{ ignores: ["lib"] },
|
|
26
|
+
eslint.configs.recommended,
|
|
27
|
+
tseslint.configs.recommendedTypeChecked,
|
|
28
|
+
eslintPlugin.configs.recommended,
|
|
29
|
+
tseslint.configs.strictTypeChecked,
|
|
30
|
+
effectEslint.configs.dprint,
|
|
31
|
+
...(suggestMembersRecommended ? [suggestMembersRecommended] : []),
|
|
32
|
+
eslintCommentsConfigs.recommended,
|
|
33
|
+
{
|
|
34
|
+
name: "analyzers",
|
|
35
|
+
languageOptions: {
|
|
36
|
+
parser: tseslint.parser,
|
|
37
|
+
globals: { ...globals.node, ...globals.browser },
|
|
38
|
+
parserOptions: {
|
|
39
|
+
projectService: {
|
|
40
|
+
allowDefaultProject: ["*.config.*"],
|
|
41
|
+
defaultProject: "tsconfig.json",
|
|
42
|
+
},
|
|
43
|
+
tsconfigRootDir: import.meta.dirname,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
plugins: {
|
|
47
|
+
sonarjs,
|
|
48
|
+
unicorn,
|
|
49
|
+
import: fixupPluginRules(importPlugin),
|
|
50
|
+
"sort-destructure-keys": sortDestructureKeys,
|
|
51
|
+
"simple-import-sort": simpleImportSort,
|
|
52
|
+
codegen: codegenPlugin,
|
|
53
|
+
},
|
|
54
|
+
files: ["**/*.ts", '**/*.{test,spec}.{ts,tsx}', '**/tests/**', '**/__tests__/**'],
|
|
55
|
+
settings: {
|
|
56
|
+
"import/parsers": {
|
|
57
|
+
"@typescript-eslint/parser": [".ts", ".tsx"],
|
|
58
|
+
},
|
|
59
|
+
"import/resolver": {
|
|
60
|
+
typescript: {
|
|
61
|
+
alwaysTryTypes: true,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
rules: {
|
|
66
|
+
...sonarjs.configs.recommended.rules,
|
|
67
|
+
...unicorn.configs.recommended.rules,
|
|
68
|
+
"no-restricted-imports": ["error", {
|
|
69
|
+
paths: [
|
|
70
|
+
{
|
|
71
|
+
name: "ts-pattern",
|
|
72
|
+
message: "Use Effect.Match instead of ts-pattern.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "zod",
|
|
76
|
+
message: "Use @effect/schema for schemas and validation.",
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
}],
|
|
80
|
+
"codegen/codegen": "error",
|
|
81
|
+
"import/first": "error",
|
|
82
|
+
"import/newline-after-import": "error",
|
|
83
|
+
"import/no-duplicates": "error",
|
|
84
|
+
"import/no-unresolved": "off",
|
|
85
|
+
"import/order": "off",
|
|
86
|
+
"simple-import-sort/imports": "off",
|
|
87
|
+
"sort-destructure-keys/sort-destructure-keys": "error",
|
|
88
|
+
"no-fallthrough": "off",
|
|
89
|
+
"no-irregular-whitespace": "off",
|
|
90
|
+
"object-shorthand": "error",
|
|
91
|
+
"prefer-destructuring": "off",
|
|
92
|
+
"sort-imports": "off",
|
|
93
|
+
"no-unused-vars": "off",
|
|
94
|
+
"prefer-rest-params": "off",
|
|
95
|
+
"prefer-spread": "off",
|
|
96
|
+
"unicorn/prefer-top-level-await": "off",
|
|
97
|
+
"unicorn/prevent-abbreviations": "off",
|
|
98
|
+
"unicorn/no-null": "off",
|
|
99
|
+
complexity: ["error", 8],
|
|
100
|
+
"max-lines-per-function": [
|
|
101
|
+
"error",
|
|
102
|
+
{ max: 50, skipBlankLines: true, skipComments: true },
|
|
103
|
+
],
|
|
104
|
+
"max-params": ["error", 5],
|
|
105
|
+
"max-depth": ["error", 4],
|
|
106
|
+
"max-lines": [
|
|
107
|
+
"error",
|
|
108
|
+
{ max: 300, skipBlankLines: true, skipComments: true },
|
|
109
|
+
],
|
|
110
|
+
|
|
111
|
+
"@typescript-eslint/restrict-template-expressions": ["error", {
|
|
112
|
+
allowNumber: true,
|
|
113
|
+
allowBoolean: true,
|
|
114
|
+
allowNullish: false,
|
|
115
|
+
allowAny: false,
|
|
116
|
+
allowRegExp: false
|
|
117
|
+
}],
|
|
118
|
+
"@eslint-community/eslint-comments/no-use": "error",
|
|
119
|
+
"@eslint-community/eslint-comments/no-unlimited-disable": "error",
|
|
120
|
+
"@eslint-community/eslint-comments/disable-enable-pair": "error",
|
|
121
|
+
"@eslint-community/eslint-comments/no-unused-disable": "error",
|
|
122
|
+
"no-restricted-syntax": [
|
|
123
|
+
"error",
|
|
124
|
+
{
|
|
125
|
+
selector: "TSUnknownKeyword",
|
|
126
|
+
message: "Запрещено 'unknown'.",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
selector: "TryStatement",
|
|
130
|
+
message: "Используй Effect.try / catchAll вместо try/catch в core/app/domain.",
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
selector: "SwitchStatement",
|
|
134
|
+
message: [
|
|
135
|
+
"Switch statements are forbidden in functional programming paradigm.",
|
|
136
|
+
"How to fix: Use Effect.Match instead.",
|
|
137
|
+
"Example:",
|
|
138
|
+
" import { Match } from 'effect';",
|
|
139
|
+
" type Item = { type: 'this' } | { type: 'that' };",
|
|
140
|
+
" const result = Match.value(item).pipe(",
|
|
141
|
+
" Match.when({ type: 'this' }, (it) => processThis(it)),",
|
|
142
|
+
" Match.when({ type: 'that' }, (it) => processThat(it)),",
|
|
143
|
+
" Match.exhaustive,",
|
|
144
|
+
" );",
|
|
145
|
+
].join("\n"),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
selector: 'CallExpression[callee.name="require"]',
|
|
149
|
+
message: "Avoid using require(). Use ES6 imports instead.",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
selector: "ThrowStatement > Literal:not([value=/^\\w+Error:/])",
|
|
153
|
+
message:
|
|
154
|
+
'Do not throw string literals or non-Error objects. Throw new Error("...") instead.',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
selector:
|
|
158
|
+
"FunctionDeclaration[async=true], FunctionExpression[async=true], ArrowFunctionExpression[async=true]",
|
|
159
|
+
message:
|
|
160
|
+
"Запрещён async/await — используй Effect.gen / Effect.tryPromise.",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
selector: "NewExpression[callee.name='Promise']",
|
|
164
|
+
message:
|
|
165
|
+
"Запрещён new Promise — используй Effect.async / Effect.tryPromise.",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
selector: "CallExpression[callee.object.name='Promise']",
|
|
169
|
+
message:
|
|
170
|
+
"Запрещены Promise.* — используй комбинаторы Effect (all, forEach, etc.).",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
selector: "CallExpression[callee.property.name='push'] > SpreadElement.arguments",
|
|
174
|
+
message: "Do not use spread arguments in Array.push",
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
"no-throw-literal": "error",
|
|
178
|
+
"@typescript-eslint/no-restricted-types": [
|
|
179
|
+
"error",
|
|
180
|
+
{
|
|
181
|
+
types: {
|
|
182
|
+
unknown: {
|
|
183
|
+
message:
|
|
184
|
+
"Не используем 'unknown'. Уточни тип или наведи порядок в источнике данных.",
|
|
185
|
+
},
|
|
186
|
+
Promise: {
|
|
187
|
+
message: "Запрещён Promise — используй Effect.Effect<A, E, R>.",
|
|
188
|
+
suggest: ["Effect.Effect"],
|
|
189
|
+
},
|
|
190
|
+
"Promise<*>": {
|
|
191
|
+
message:
|
|
192
|
+
"Запрещён Promise<T> — используй Effect.Effect<T, E, R>.",
|
|
193
|
+
suggest: ["Effect.Effect<T, E, R>"],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
"@typescript-eslint/use-unknown-in-catch-callback-variable": "off",
|
|
199
|
+
// "no-throw-literal": "off",
|
|
200
|
+
"@typescript-eslint/only-throw-error": [
|
|
201
|
+
"error",
|
|
202
|
+
{ allowThrowingUnknown: false, allowThrowingAny: false },
|
|
203
|
+
],
|
|
204
|
+
"@typescript-eslint/array-type": ["warn", {
|
|
205
|
+
default: "generic",
|
|
206
|
+
readonly: "generic"
|
|
207
|
+
}],
|
|
208
|
+
"@typescript-eslint/member-delimiter-style": 0,
|
|
209
|
+
"@typescript-eslint/no-non-null-assertion": "off",
|
|
210
|
+
"@typescript-eslint/ban-types": "off",
|
|
211
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
212
|
+
"@typescript-eslint/no-empty-interface": "off",
|
|
213
|
+
"@typescript-eslint/consistent-type-imports": "warn",
|
|
214
|
+
"@typescript-eslint/no-unused-vars": ["error", {
|
|
215
|
+
argsIgnorePattern: "^_",
|
|
216
|
+
varsIgnorePattern: "^_"
|
|
217
|
+
}],
|
|
218
|
+
"@typescript-eslint/ban-ts-comment": "off",
|
|
219
|
+
"@typescript-eslint/camelcase": "off",
|
|
220
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
221
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
222
|
+
"@typescript-eslint/interface-name-prefix": "off",
|
|
223
|
+
"@typescript-eslint/no-array-constructor": "off",
|
|
224
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
225
|
+
"@typescript-eslint/no-namespace": "off",
|
|
226
|
+
"@effect/dprint": ["error", {
|
|
227
|
+
config: {
|
|
228
|
+
indentWidth: 2,
|
|
229
|
+
lineWidth: 120,
|
|
230
|
+
semiColons: "asi",
|
|
231
|
+
quoteStyle: "alwaysDouble",
|
|
232
|
+
trailingCommas: "never",
|
|
233
|
+
operatorPosition: "maintain",
|
|
234
|
+
"arrowFunction.useParentheses": "force"
|
|
235
|
+
}
|
|
236
|
+
}]
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
files: ['**/*.{test,spec}.{ts,tsx}', 'tests/**', '**/__tests__/**'],
|
|
241
|
+
...vitest.configs.all,
|
|
242
|
+
languageOptions: {
|
|
243
|
+
globals: {
|
|
244
|
+
...vitest.environments.env.globals,
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
rules: {
|
|
248
|
+
// Allow eslint-disable/enable comments in test files for fine-grained control
|
|
249
|
+
'@eslint-community/eslint-comments/no-use': 'off',
|
|
250
|
+
// Disable line count limit for E2E tests that contain multiple test cases
|
|
251
|
+
'max-lines-per-function': 'off',
|
|
252
|
+
// `it.effect` is not recognized by sonar rule; disable to avoid false positives
|
|
253
|
+
'sonarjs/no-empty-test-file': 'off',
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// 3) Для JS-файлов отключим типо-зависимые проверки
|
|
258
|
+
{
|
|
259
|
+
files: ['**/*.{js,cjs,mjs}'],
|
|
260
|
+
extends: [tseslint.configs.disableTypeChecked],
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
// 4) Глобальные игноры
|
|
264
|
+
{ ignores: ['dist/**', 'build/**', 'coverage/**', '**/dist/**'] },
|
|
265
|
+
);
|