@tsfpp/eslint-config 1.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/README.md +116 -0
- package/dist/api.d.ts +27 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +74 -0
- package/dist/api.js.map +1 -0
- package/dist/base.d.ts +26 -0
- package/dist/base.d.ts.map +1 -0
- package/dist/base.js +179 -0
- package/dist/base.js.map +1 -0
- package/dist/plugin-compat.d.ts +30 -0
- package/dist/plugin-compat.d.ts.map +1 -0
- package/dist/plugin-compat.js +30 -0
- package/dist/plugin-compat.js.map +1 -0
- package/dist/react.d.ts +31 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +115 -0
- package/dist/react.js.map +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# @tsfpp/eslint-config
|
|
2
|
+
|
|
3
|
+
Shared ESLint flat configuration for [TSF++](../../spec/standards/TSFPP_CODING_STANDARD.md) projects.
|
|
4
|
+
|
|
5
|
+
Enforces the machine-checkable subset of TSF++ rules via `@typescript-eslint` and `eslint-plugin-functional`. Every active rule references the spec rule it enforces.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- ESLint ≥ 9 (flat config format)
|
|
10
|
+
- TypeScript ≥ 5.3
|
|
11
|
+
- A `tsconfig.json` reachable from each linted file
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm add -D @tsfpp/eslint-config \
|
|
17
|
+
eslint \
|
|
18
|
+
typescript \
|
|
19
|
+
@typescript-eslint/parser \
|
|
20
|
+
@typescript-eslint/eslint-plugin \
|
|
21
|
+
eslint-plugin-functional
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Standalone
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
// eslint.config.js
|
|
30
|
+
import tsfpp from '@tsfpp/eslint-config'
|
|
31
|
+
|
|
32
|
+
export default tsfpp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### With project-specific overrides
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
// eslint.config.js
|
|
39
|
+
import tsfpp from '@tsfpp/eslint-config'
|
|
40
|
+
|
|
41
|
+
export default [
|
|
42
|
+
...tsfpp,
|
|
43
|
+
{
|
|
44
|
+
// Relax console warnings in scripts
|
|
45
|
+
rules: { 'no-console': 'off' },
|
|
46
|
+
},
|
|
47
|
+
]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### With `@tsfpp/tsconfig`
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
// eslint.config.js
|
|
54
|
+
import tsfpp from '@tsfpp/eslint-config'
|
|
55
|
+
|
|
56
|
+
export default [
|
|
57
|
+
...tsfpp,
|
|
58
|
+
{
|
|
59
|
+
languageOptions: {
|
|
60
|
+
parserOptions: {
|
|
61
|
+
// Point to your project tsconfig explicitly if needed
|
|
62
|
+
project: './tsconfig.json',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Enforced rules
|
|
70
|
+
|
|
71
|
+
| ESLint rule | TSF++ rule | Level |
|
|
72
|
+
|-------------|------------|-------|
|
|
73
|
+
| `@typescript-eslint/no-explicit-any` | 1.5 | MUST |
|
|
74
|
+
| `@typescript-eslint/no-non-null-assertion` | 1.6 | MUST |
|
|
75
|
+
| `@typescript-eslint/consistent-type-assertions: never` | 1.6 | MUST |
|
|
76
|
+
| `@typescript-eslint/switch-exhaustiveness-check` | 1.2 / 4.1 | MUST |
|
|
77
|
+
| `@typescript-eslint/explicit-function-return-type` | 3.1 | MUST |
|
|
78
|
+
| `@typescript-eslint/strict-boolean-expressions` | 4.5 | MUST |
|
|
79
|
+
| `@typescript-eslint/no-floating-promises` | 6.4 | SHOULD |
|
|
80
|
+
| `@typescript-eslint/prefer-readonly` | 2.2 | MUST |
|
|
81
|
+
| `functional/no-let` | 2.1 | MUST |
|
|
82
|
+
| `functional/no-loop-statements` | 4.2 | MUST |
|
|
83
|
+
| `functional/no-throw-statements` | 6.2 | MUST |
|
|
84
|
+
| `functional/prefer-readonly-type` | 2.2 | MUST |
|
|
85
|
+
| `functional/immutable-data` | 2.3 | MUST |
|
|
86
|
+
| `functional/no-classes` | 1.9 | MUST |
|
|
87
|
+
| `functional/no-this-expressions` | 1.9 | MUST |
|
|
88
|
+
| `complexity: 10` | 3.4 | MUST |
|
|
89
|
+
| `max-depth: 4` | 3.4 | MUST |
|
|
90
|
+
| `max-lines-per-function: 40` | 3.4 | MUST |
|
|
91
|
+
| `eqeqeq: always` | 4.5 | MUST |
|
|
92
|
+
| `no-param-reassign` | 2.3 | MUST |
|
|
93
|
+
|
|
94
|
+
## Handling permitted deviations
|
|
95
|
+
|
|
96
|
+
Some TSF++ rules permit deviations at specific boundaries. Use `eslint-disable-next-line` with a paired `DEVIATION` comment so deviations are explicit and auditable:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// DEVIATION(1.6): `as` permitted inside smart constructor boundary
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
101
|
+
return raw as NodeId
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// DEVIATION(6.2): adapter boundary — wraps third-party throw in Result
|
|
106
|
+
// eslint-disable-next-line functional/no-throw-statements
|
|
107
|
+
throw new Error('unreachable')
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This makes every deviation visible to the TSF++ Audit agent and during code review.
|
|
111
|
+
|
|
112
|
+
## See also
|
|
113
|
+
|
|
114
|
+
- [spec/standards/TSFPP_CODING_STANDARD.md §9](../../spec/standards/TSFPP_CODING_STANDARD.md#9--compiler-and-tooling-configuration) — normative rule reference
|
|
115
|
+
- [spec/rationale/09-tooling.md](../../spec/rationale/09-tooling.md) — extended justification
|
|
116
|
+
- [@tsfpp/tsconfig](../tsconfig/README.md) — companion TypeScript config presets
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config/api
|
|
3
|
+
*
|
|
4
|
+
* TSF++ ESLint configuration profile for HTTP API / Node.js server code.
|
|
5
|
+
* Extends the base config with rules that enforce stricter output hygiene,
|
|
6
|
+
* async correctness, and boundary discipline required by API_CODING_STANDARD.md.
|
|
7
|
+
*
|
|
8
|
+
* Rule references in this file are to API_CODING_STANDARD.md unless prefixed with "TSF++".
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* // eslint.config.js
|
|
12
|
+
* import tsfppApi from '@tsfpp/eslint-config/api'
|
|
13
|
+
* export default tsfppApi
|
|
14
|
+
*
|
|
15
|
+
* With project-specific overrides:
|
|
16
|
+
* import tsfppApi from '@tsfpp/eslint-config/api'
|
|
17
|
+
* export default [
|
|
18
|
+
* ...tsfppApi,
|
|
19
|
+
* { files: ['src/adapters/**'], rules: { 'no-console': 'off' } },
|
|
20
|
+
* ]
|
|
21
|
+
*
|
|
22
|
+
* No additional devDependencies are required beyond the base package requirements.
|
|
23
|
+
*/
|
|
24
|
+
import type { Linter } from 'eslint';
|
|
25
|
+
declare const config: Linter.Config[];
|
|
26
|
+
export default config;
|
|
27
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAGpC,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EA6D1B,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config/api
|
|
3
|
+
*
|
|
4
|
+
* TSF++ ESLint configuration profile for HTTP API / Node.js server code.
|
|
5
|
+
* Extends the base config with rules that enforce stricter output hygiene,
|
|
6
|
+
* async correctness, and boundary discipline required by API_CODING_STANDARD.md.
|
|
7
|
+
*
|
|
8
|
+
* Rule references in this file are to API_CODING_STANDARD.md unless prefixed with "TSF++".
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* // eslint.config.js
|
|
12
|
+
* import tsfppApi from '@tsfpp/eslint-config/api'
|
|
13
|
+
* export default tsfppApi
|
|
14
|
+
*
|
|
15
|
+
* With project-specific overrides:
|
|
16
|
+
* import tsfppApi from '@tsfpp/eslint-config/api'
|
|
17
|
+
* export default [
|
|
18
|
+
* ...tsfppApi,
|
|
19
|
+
* { files: ['src/adapters/**'], rules: { 'no-console': 'off' } },
|
|
20
|
+
* ]
|
|
21
|
+
*
|
|
22
|
+
* No additional devDependencies are required beyond the base package requirements.
|
|
23
|
+
*/
|
|
24
|
+
import base from './base.js';
|
|
25
|
+
const config = [
|
|
26
|
+
// ── Inherit all base TSF++ rules ─────────────────────────────────────────────
|
|
27
|
+
...base,
|
|
28
|
+
// ── API-specific overrides and additions ──────────────────────────────────────
|
|
29
|
+
{
|
|
30
|
+
files: ['**/*.ts'],
|
|
31
|
+
rules: {
|
|
32
|
+
// ── Output / logging hygiene ────────────────────────────────────────────
|
|
33
|
+
// API servers must never use console.* — structured logging only (withLogging from logger.ts).
|
|
34
|
+
// The base config sets this to 'warn'; API elevates to 'error'.
|
|
35
|
+
'no-console': 'error',
|
|
36
|
+
// ── Async correctness (Rules 6.x, TSF++ Rule 6.4) ──────────────────────
|
|
37
|
+
// Every async function must contain at least one await expression.
|
|
38
|
+
// Prevents accidentally marking synchronous handlers as async.
|
|
39
|
+
'@typescript-eslint/require-await': 'error',
|
|
40
|
+
// Returned promises must be awaited (catches fire-and-forget in handlers).
|
|
41
|
+
'@typescript-eslint/return-await': ['error', 'always'],
|
|
42
|
+
// ── Boundary strictness (Rules 1.x, 5.x) ───────────────────────────────
|
|
43
|
+
// API rule 5.1 — every request body / query param must pass through Zod (or equivalent).
|
|
44
|
+
// Enforced structurally; no ESLint rule maps cleanly without custom plugin.
|
|
45
|
+
// Document this at the code-review gate (API_CODING_STANDARD.md §10 checklist).
|
|
46
|
+
// Disallow usage of unsafe any-typed values passed to typed parameters.
|
|
47
|
+
// Belt-and-suspenders at parse/validate boundaries.
|
|
48
|
+
'@typescript-eslint/no-unsafe-argument': 'error',
|
|
49
|
+
'@typescript-eslint/no-unsafe-assignment': 'error',
|
|
50
|
+
'@typescript-eslint/no-unsafe-call': 'error',
|
|
51
|
+
'@typescript-eslint/no-unsafe-member-access': 'error',
|
|
52
|
+
'@typescript-eslint/no-unsafe-return': 'error',
|
|
53
|
+
// ── Error handling (TSF++ Rule 6.2; API Rule 4.x) ──────────────────────
|
|
54
|
+
// functional/no-throw-statements is already 'error' in base.
|
|
55
|
+
// No additional override required; adapter boundaries use DEVIATION(6.2).
|
|
56
|
+
// Prevent Promise rejection with non-Error values.
|
|
57
|
+
'prefer-promise-reject-errors': 'error',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
// ── Adapter / boundary files — allow `new` and `throw` with deviation ─────────
|
|
61
|
+
// Projects that follow TSF++ boundary conventions will place all I/O adapters in
|
|
62
|
+
// a dedicated directory (e.g. src/adapters/, src/infrastructure/).
|
|
63
|
+
// Override per-project in the consuming eslint.config.js if needed:
|
|
64
|
+
//
|
|
65
|
+
// {
|
|
66
|
+
// files: ['src/adapters/**'],
|
|
67
|
+
// rules: {
|
|
68
|
+
// 'no-restricted-syntax': 'off', // allows `new` and `instanceof`
|
|
69
|
+
// 'functional/no-throw-statements': 'off', // allows `throw` at boundary
|
|
70
|
+
// },
|
|
71
|
+
// },
|
|
72
|
+
];
|
|
73
|
+
export default config;
|
|
74
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,IAAI,MAAiB,WAAW,CAAA;AAEvC,MAAM,MAAM,GAAoB;IAE9B,gFAAgF;IAChF,GAAG,IAAI;IAEP,iFAAiF;IACjF;QACE,KAAK,EAAE,CAAC,SAAS,CAAC;QAClB,KAAK,EAAE;YAEL,2EAA2E;YAE3E,+FAA+F;YAC/F,gEAAgE;YAChE,YAAY,EAAE,OAAO;YAErB,0EAA0E;YAE1E,mEAAmE;YACnE,+DAA+D;YAC/D,kCAAkC,EAAE,OAAO;YAE3C,2EAA2E;YAC3E,iCAAiC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;YAEtD,0EAA0E;YAE1E,yFAAyF;YACzF,4EAA4E;YAC5E,gFAAgF;YAEhF,wEAAwE;YACxE,oDAAoD;YACpD,uCAAuC,EAAI,OAAO;YAClD,yCAAyC,EAAG,OAAO;YACnD,mCAAmC,EAAS,OAAO;YACnD,4CAA4C,EAAE,OAAO;YACrD,qCAAqC,EAAO,OAAO;YAEnD,0EAA0E;YAE1E,6DAA6D;YAC7D,0EAA0E;YAE1E,mDAAmD;YACnD,8BAA8B,EAAE,OAAO;SACxC;KACF;IAED,iFAAiF;IACjF,iFAAiF;IACjF,mEAAmE;IACnE,oEAAoE;IACpE,EAAE;IACF,MAAM;IACN,kCAAkC;IAClC,eAAe;IACf,mFAAmF;IACnF,gFAAgF;IAChF,SAAS;IACT,OAAO;CACR,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/base.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config — base
|
|
3
|
+
*
|
|
4
|
+
* Shared ESLint flat configuration for TSF++ projects (pure TypeScript targets).
|
|
5
|
+
* Enforces Rules 1.2–1.9, 2.1–2.3, 3.1–3.2, 3.4, 4.1, 4.2, 4.5, 6.2, 6.4, 11.2
|
|
6
|
+
* from CODING_STANDARD.md via @typescript-eslint and eslint-plugin-functional.
|
|
7
|
+
*
|
|
8
|
+
* Usage (base only):
|
|
9
|
+
* // eslint.config.js
|
|
10
|
+
* import tsfpp from '@tsfpp/eslint-config'
|
|
11
|
+
* export default tsfpp
|
|
12
|
+
*
|
|
13
|
+
* Usage with project-specific overrides:
|
|
14
|
+
* import tsfpp from '@tsfpp/eslint-config'
|
|
15
|
+
* export default [
|
|
16
|
+
* ...tsfpp,
|
|
17
|
+
* { rules: { 'no-console': 'off' } },
|
|
18
|
+
* ]
|
|
19
|
+
*
|
|
20
|
+
* For React projects, import '@tsfpp/eslint-config/react' instead.
|
|
21
|
+
* For API projects, import '@tsfpp/eslint-config/api' instead.
|
|
22
|
+
*/
|
|
23
|
+
import type { Linter } from 'eslint';
|
|
24
|
+
declare const config: Linter.Config[];
|
|
25
|
+
export default config;
|
|
26
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../base.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAO,QAAQ,CAAA;AAMrC,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EA0L1B,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/base.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config — base
|
|
3
|
+
*
|
|
4
|
+
* Shared ESLint flat configuration for TSF++ projects (pure TypeScript targets).
|
|
5
|
+
* Enforces Rules 1.2–1.9, 2.1–2.3, 3.1–3.2, 3.4, 4.1, 4.2, 4.5, 6.2, 6.4, 11.2
|
|
6
|
+
* from CODING_STANDARD.md via @typescript-eslint and eslint-plugin-functional.
|
|
7
|
+
*
|
|
8
|
+
* Usage (base only):
|
|
9
|
+
* // eslint.config.js
|
|
10
|
+
* import tsfpp from '@tsfpp/eslint-config'
|
|
11
|
+
* export default tsfpp
|
|
12
|
+
*
|
|
13
|
+
* Usage with project-specific overrides:
|
|
14
|
+
* import tsfpp from '@tsfpp/eslint-config'
|
|
15
|
+
* export default [
|
|
16
|
+
* ...tsfpp,
|
|
17
|
+
* { rules: { 'no-console': 'off' } },
|
|
18
|
+
* ]
|
|
19
|
+
*
|
|
20
|
+
* For React projects, import '@tsfpp/eslint-config/react' instead.
|
|
21
|
+
* For API projects, import '@tsfpp/eslint-config/api' instead.
|
|
22
|
+
*/
|
|
23
|
+
import tsParser from '@typescript-eslint/parser';
|
|
24
|
+
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
|
25
|
+
import functionalPlugin from 'eslint-plugin-functional';
|
|
26
|
+
import { coercePlugin } from './plugin-compat.js';
|
|
27
|
+
const config = [
|
|
28
|
+
// ── Global ignores ────────────────────────────────────────────────────────────
|
|
29
|
+
{
|
|
30
|
+
ignores: ['dist/**', 'coverage/**', 'node_modules/**', '**/*.d.ts'],
|
|
31
|
+
},
|
|
32
|
+
// ── TypeScript source files ───────────────────────────────────────────────────
|
|
33
|
+
{
|
|
34
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
35
|
+
languageOptions: {
|
|
36
|
+
parser: tsParser,
|
|
37
|
+
parserOptions: {
|
|
38
|
+
sourceType: 'module',
|
|
39
|
+
ecmaVersion: 'latest',
|
|
40
|
+
project: true, // resolves nearest tsconfig.json; override with projectService
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
plugins: {
|
|
44
|
+
'@typescript-eslint': coercePlugin(tsPlugin),
|
|
45
|
+
'functional': coercePlugin(functionalPlugin),
|
|
46
|
+
},
|
|
47
|
+
rules: {
|
|
48
|
+
// ── Type-system rules ─────────────────────────────────────────────────────
|
|
49
|
+
// Rule 1.2 / 4.1 — exhaustive switch via never (backed by @tsfpp/prelude absurd)
|
|
50
|
+
'@typescript-eslint/switch-exhaustiveness-check': 'error',
|
|
51
|
+
// Rule 1.4 — prefer `type` over `interface`; `interface` requires DEVIATION(1.4)
|
|
52
|
+
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
|
|
53
|
+
// Rule 1.5 — no `any`
|
|
54
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
55
|
+
// Rule 1.6 — no `!`
|
|
56
|
+
'@typescript-eslint/no-non-null-assertion': 'error',
|
|
57
|
+
// Rule 1.6 — no `as` (DEVIATION(1.6) + eslint-disable required inside smart constructors)
|
|
58
|
+
'@typescript-eslint/consistent-type-assertions': ['error', {
|
|
59
|
+
assertionStyle: 'never',
|
|
60
|
+
}],
|
|
61
|
+
// Rule 1.8 / 1.9 — forbidden language constructs (no ESLint plugin covers these directly)
|
|
62
|
+
'no-restricted-syntax': [
|
|
63
|
+
'error',
|
|
64
|
+
{
|
|
65
|
+
// Rule 1.8 — no enum
|
|
66
|
+
selector: 'TSEnumDeclaration',
|
|
67
|
+
message: 'TSF++ Rule 1.8: Use string literal unions or `as const` objects instead of enum.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
// Rule 1.9 — no namespace
|
|
71
|
+
selector: "TSModuleDeclaration[kind='namespace']",
|
|
72
|
+
message: 'TSF++ Rule 1.9: Namespace declarations are forbidden.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
// Rule 1.9 — no instanceof (prefer discriminated unions and type-guard predicates)
|
|
76
|
+
selector: 'BinaryExpression[operator="instanceof"]',
|
|
77
|
+
message: 'TSF++ Rule 1.9: instanceof is forbidden; use discriminated unions or exported type-guard predicates.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
// Rule 1.9 — no new (adapter boundaries require DEVIATION(1.9) comment + eslint-disable)
|
|
81
|
+
selector: 'NewExpression',
|
|
82
|
+
message: 'TSF++ Rule 1.9: new is forbidden outside adapter boundaries. Add DEVIATION(1.9) and an eslint-disable comment.',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
// ── Immutability & functional style ──────────────────────────────────────
|
|
86
|
+
// Rule 2.1 — no `let`
|
|
87
|
+
'functional/no-let': 'error',
|
|
88
|
+
// Rule 2.2 — readonly types (deep)
|
|
89
|
+
'functional/prefer-readonly-type': ['error', {
|
|
90
|
+
allowLocalMutation: false,
|
|
91
|
+
allowMutableReturnType: false,
|
|
92
|
+
ignoreClass: false,
|
|
93
|
+
ignoreInterface: false,
|
|
94
|
+
}],
|
|
95
|
+
// Rule 2.2 — prefer-readonly on (legacy) class properties (belt-and-suspenders)
|
|
96
|
+
'@typescript-eslint/prefer-readonly': 'error',
|
|
97
|
+
// Rule 2.3 — no mutation (property assignment, array mutation methods)
|
|
98
|
+
'functional/immutable-data': ['error', {
|
|
99
|
+
ignoreImmediateMutation: true, // allows builder pattern inside same statement
|
|
100
|
+
ignoreAccessorPattern: ['**.current', 'process.env.*'], // React refs + env edge cases
|
|
101
|
+
}],
|
|
102
|
+
// Rule 1.9 — no classes / no this
|
|
103
|
+
'functional/no-classes': 'error',
|
|
104
|
+
'functional/no-this-expressions': 'error',
|
|
105
|
+
// Rule 4.2 — no imperative loops
|
|
106
|
+
'functional/no-loop-statements': 'error',
|
|
107
|
+
// Rule 6.2 — no throw outside adapter boundaries
|
|
108
|
+
// Disable per-file with `// eslint-disable-next-line functional/no-throw-statements`
|
|
109
|
+
// accompanied by a `// DEVIATION(6.2)` comment at every adapter entry point.
|
|
110
|
+
'functional/no-throw-statements': 'error',
|
|
111
|
+
// ── Function rules ────────────────────────────────────────────────────────
|
|
112
|
+
// Rule 3.1 — explicit return type on every exported function
|
|
113
|
+
'@typescript-eslint/explicit-function-return-type': ['error', {
|
|
114
|
+
allowExpressions: false,
|
|
115
|
+
allowTypedFunctionExpressions: true,
|
|
116
|
+
allowHigherOrderFunctions: true,
|
|
117
|
+
allowDirectConstAssertionInArrowFunctions: true,
|
|
118
|
+
}],
|
|
119
|
+
// Rule 3.2 — arity ≤ 3; use a readonly record for ≥ 3 parameters
|
|
120
|
+
'max-params': ['error', { max: 3 }],
|
|
121
|
+
// Rule 3.4 — cyclomatic complexity ≤ 10
|
|
122
|
+
'complexity': ['error', { max: 10 }],
|
|
123
|
+
// Rule 3.4 — nesting depth ≤ 4
|
|
124
|
+
'max-depth': ['error', { max: 4 }],
|
|
125
|
+
// Rule 3.4 — 40-line function body limit
|
|
126
|
+
'max-lines-per-function': ['error', {
|
|
127
|
+
max: 40,
|
|
128
|
+
skipBlankLines: true,
|
|
129
|
+
skipComments: true,
|
|
130
|
+
}],
|
|
131
|
+
// ── Async / promise safety ────────────────────────────────────────────────
|
|
132
|
+
// Rule 6.4 — no unhandled promise rejections
|
|
133
|
+
'@typescript-eslint/no-floating-promises': 'error',
|
|
134
|
+
'@typescript-eslint/no-misused-promises': 'error',
|
|
135
|
+
// ── Control-flow hygiene ──────────────────────────────────────────────────
|
|
136
|
+
// Rule 4.5 — no truthiness checks on non-booleans
|
|
137
|
+
'@typescript-eslint/strict-boolean-expressions': ['error', {
|
|
138
|
+
allowString: false,
|
|
139
|
+
allowNumber: false,
|
|
140
|
+
allowNullableObject: false,
|
|
141
|
+
allowNullableBoolean: false,
|
|
142
|
+
allowNullableString: false,
|
|
143
|
+
allowNullableNumber: false,
|
|
144
|
+
allowAny: false,
|
|
145
|
+
}],
|
|
146
|
+
// ── Module hygiene ────────────────────────────────────────────────────────
|
|
147
|
+
// Prefer `import type` for type-only imports (verbatimModuleSyntax companion)
|
|
148
|
+
'@typescript-eslint/consistent-type-imports': ['error', {
|
|
149
|
+
prefer: 'type-imports',
|
|
150
|
+
fixStyle: 'inline-type-imports',
|
|
151
|
+
disallowTypeAnnotations: false,
|
|
152
|
+
}],
|
|
153
|
+
// ── General hygiene ───────────────────────────────────────────────────────
|
|
154
|
+
// Accidental console.log in production code — warn in base; API profile overrides to error
|
|
155
|
+
'no-console': 'warn',
|
|
156
|
+
// Rule 2.1 — const only
|
|
157
|
+
'prefer-const': 'error',
|
|
158
|
+
'no-var': 'error',
|
|
159
|
+
// Rule 2.3 — no parameter reassignment
|
|
160
|
+
'no-param-reassign': 'error',
|
|
161
|
+
// Rule 4.5 — strict equality
|
|
162
|
+
'eqeqeq': ['error', 'always'],
|
|
163
|
+
// Rule 11.2 — file length ≤ 400 lines (800 absolute maximum via DEVIATION(11.2))
|
|
164
|
+
'max-lines': ['error', {
|
|
165
|
+
max: 400,
|
|
166
|
+
skipBlankLines: true,
|
|
167
|
+
skipComments: true,
|
|
168
|
+
}],
|
|
169
|
+
// Unused vars — `_` prefix exempted (Rule 7.6)
|
|
170
|
+
'@typescript-eslint/no-unused-vars': ['error', {
|
|
171
|
+
argsIgnorePattern: '^_',
|
|
172
|
+
varsIgnorePattern: '^_',
|
|
173
|
+
caughtErrorsIgnorePattern: '^_',
|
|
174
|
+
}],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
];
|
|
178
|
+
export default config;
|
|
179
|
+
//# sourceMappingURL=base.js.map
|
package/dist/base.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugin-compat.ts
|
|
3
|
+
*
|
|
4
|
+
* Adapter boundary for third-party ESLint plugin type coercion.
|
|
5
|
+
*
|
|
6
|
+
* Problem: ESLint plugin packages frequently ship TypeScript types that lag
|
|
7
|
+
* behind the current @eslint/core `Plugin` interface. The structural mismatch
|
|
8
|
+
* (e.g. `null` in `parser` where `string | undefined` is expected, or `flat`
|
|
9
|
+
* keys in `configs` that don't satisfy the index signature) causes TS2322
|
|
10
|
+
* errors at plugin registration sites in flat config objects.
|
|
11
|
+
*
|
|
12
|
+
* Solution: Isolate the necessary `as` cast here, at this single adapter
|
|
13
|
+
* boundary. Every callsite in base.ts / react.ts / api.ts is auditable by
|
|
14
|
+
* grepping for `coercePlugin`.
|
|
15
|
+
*
|
|
16
|
+
* DEVIATION(1.6): `as` cast is intentional and confined to this module.
|
|
17
|
+
* Justification: third-party type incompatibility at an adapter boundary;
|
|
18
|
+
* no runtime effect — this is a pure type-level coercion with zero behavior.
|
|
19
|
+
* Reviewed and approved per TSF++ deviation procedure.
|
|
20
|
+
*/
|
|
21
|
+
import type { ESLint } from 'eslint';
|
|
22
|
+
/**
|
|
23
|
+
* Coerces a third-party ESLint plugin object to the canonical `ESLint.Plugin`
|
|
24
|
+
* type accepted by flat config `plugins` records.
|
|
25
|
+
*
|
|
26
|
+
* Use exclusively at plugin registration sites. Never use to silence unrelated
|
|
27
|
+
* type errors.
|
|
28
|
+
*/
|
|
29
|
+
export declare const coercePlugin: (plugin: unknown) => ESLint.Plugin;
|
|
30
|
+
//# sourceMappingURL=plugin-compat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-compat.d.ts","sourceRoot":"","sources":["../plugin-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEpC;;;;;;GAMG;AAEH,eAAO,MAAM,YAAY,GAAI,QAAQ,OAAO,KAAG,MAAM,CAAC,MAC7B,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugin-compat.ts
|
|
3
|
+
*
|
|
4
|
+
* Adapter boundary for third-party ESLint plugin type coercion.
|
|
5
|
+
*
|
|
6
|
+
* Problem: ESLint plugin packages frequently ship TypeScript types that lag
|
|
7
|
+
* behind the current @eslint/core `Plugin` interface. The structural mismatch
|
|
8
|
+
* (e.g. `null` in `parser` where `string | undefined` is expected, or `flat`
|
|
9
|
+
* keys in `configs` that don't satisfy the index signature) causes TS2322
|
|
10
|
+
* errors at plugin registration sites in flat config objects.
|
|
11
|
+
*
|
|
12
|
+
* Solution: Isolate the necessary `as` cast here, at this single adapter
|
|
13
|
+
* boundary. Every callsite in base.ts / react.ts / api.ts is auditable by
|
|
14
|
+
* grepping for `coercePlugin`.
|
|
15
|
+
*
|
|
16
|
+
* DEVIATION(1.6): `as` cast is intentional and confined to this module.
|
|
17
|
+
* Justification: third-party type incompatibility at an adapter boundary;
|
|
18
|
+
* no runtime effect — this is a pure type-level coercion with zero behavior.
|
|
19
|
+
* Reviewed and approved per TSF++ deviation procedure.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Coerces a third-party ESLint plugin object to the canonical `ESLint.Plugin`
|
|
23
|
+
* type accepted by flat config `plugins` records.
|
|
24
|
+
*
|
|
25
|
+
* Use exclusively at plugin registration sites. Never use to silence unrelated
|
|
26
|
+
* type errors.
|
|
27
|
+
*/
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
29
|
+
export const coercePlugin = (plugin) => plugin; // DEVIATION(1.6): see module JSDoc above
|
|
30
|
+
//# sourceMappingURL=plugin-compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-compat.js","sourceRoot":"","sources":["../plugin-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;GAMG;AACH,yEAAyE;AACzE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAe,EAAiB,EAAE,CAC7D,MAAuB,CAAA,CAAE,yCAAyC"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config/react
|
|
3
|
+
*
|
|
4
|
+
* TSF++ ESLint configuration profile for React / TSX code.
|
|
5
|
+
* Extends the base config with React-, hooks-, accessibility-, and TanStack Query–specific rules.
|
|
6
|
+
*
|
|
7
|
+
* Enforces rules from REACT_CODING_STANDARD.md (a profile of CODING_STANDARD.md).
|
|
8
|
+
* Rule references in this file are to REACT_CODING_STANDARD.md unless prefixed with "TSF++".
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* // eslint.config.js
|
|
12
|
+
* import tsfppReact from '@tsfpp/eslint-config/react'
|
|
13
|
+
* export default tsfppReact
|
|
14
|
+
*
|
|
15
|
+
* With project-specific overrides:
|
|
16
|
+
* import tsfppReact from '@tsfpp/eslint-config/react'
|
|
17
|
+
* export default [
|
|
18
|
+
* ...tsfppReact,
|
|
19
|
+
* { rules: { 'react/display-name': 'off' } },
|
|
20
|
+
* ]
|
|
21
|
+
*
|
|
22
|
+
* Required devDependencies (in addition to base requirements):
|
|
23
|
+
* eslint-plugin-react
|
|
24
|
+
* eslint-plugin-react-hooks
|
|
25
|
+
* eslint-plugin-jsx-a11y
|
|
26
|
+
* @tanstack/eslint-plugin-query
|
|
27
|
+
*/
|
|
28
|
+
import type { Linter } from 'eslint';
|
|
29
|
+
declare const config: Linter.Config[];
|
|
30
|
+
export default config;
|
|
31
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../react.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAS,QAAQ,CAAA;AASvC,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAqG1B,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config/react
|
|
3
|
+
*
|
|
4
|
+
* TSF++ ESLint configuration profile for React / TSX code.
|
|
5
|
+
* Extends the base config with React-, hooks-, accessibility-, and TanStack Query–specific rules.
|
|
6
|
+
*
|
|
7
|
+
* Enforces rules from REACT_CODING_STANDARD.md (a profile of CODING_STANDARD.md).
|
|
8
|
+
* Rule references in this file are to REACT_CODING_STANDARD.md unless prefixed with "TSF++".
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* // eslint.config.js
|
|
12
|
+
* import tsfppReact from '@tsfpp/eslint-config/react'
|
|
13
|
+
* export default tsfppReact
|
|
14
|
+
*
|
|
15
|
+
* With project-specific overrides:
|
|
16
|
+
* import tsfppReact from '@tsfpp/eslint-config/react'
|
|
17
|
+
* export default [
|
|
18
|
+
* ...tsfppReact,
|
|
19
|
+
* { rules: { 'react/display-name': 'off' } },
|
|
20
|
+
* ]
|
|
21
|
+
*
|
|
22
|
+
* Required devDependencies (in addition to base requirements):
|
|
23
|
+
* eslint-plugin-react
|
|
24
|
+
* eslint-plugin-react-hooks
|
|
25
|
+
* eslint-plugin-jsx-a11y
|
|
26
|
+
* @tanstack/eslint-plugin-query
|
|
27
|
+
*/
|
|
28
|
+
import reactPlugin from 'eslint-plugin-react';
|
|
29
|
+
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
30
|
+
// eslint-plugin-jsx-a11y ships no types — declared in declarations.d.ts
|
|
31
|
+
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
|
|
32
|
+
import tanstackQueryPlugin from '@tanstack/eslint-plugin-query';
|
|
33
|
+
import base from './base.js';
|
|
34
|
+
import { coercePlugin } from './plugin-compat.js';
|
|
35
|
+
const config = [
|
|
36
|
+
// ── Inherit all base TSF++ rules (applies to .ts and .tsx) ───────────────────
|
|
37
|
+
...base,
|
|
38
|
+
// ── React / TSX layer ────────────────────────────────────────────────────────
|
|
39
|
+
{
|
|
40
|
+
files: ['**/*.tsx'],
|
|
41
|
+
plugins: {
|
|
42
|
+
'react': coercePlugin(reactPlugin),
|
|
43
|
+
'react-hooks': coercePlugin(reactHooksPlugin),
|
|
44
|
+
'jsx-a11y': coercePlugin(jsxA11yPlugin),
|
|
45
|
+
'@tanstack/query': coercePlugin(tanstackQueryPlugin),
|
|
46
|
+
},
|
|
47
|
+
settings: {
|
|
48
|
+
react: { version: 'detect' },
|
|
49
|
+
},
|
|
50
|
+
rules: {
|
|
51
|
+
// ── Component shape (Rules 1.1–1.2) ────────────────────────────────────
|
|
52
|
+
// Rule 1.1 — no function declarations for components (must be arrow const)
|
|
53
|
+
// `@typescript-eslint/explicit-function-return-type` already enforces the return type half;
|
|
54
|
+
// this prevents the function-declaration form.
|
|
55
|
+
'react/function-component-definition': ['error', {
|
|
56
|
+
namedComponents: 'arrow-function',
|
|
57
|
+
unnamedComponents: 'arrow-function',
|
|
58
|
+
}],
|
|
59
|
+
// JSX transform — no explicit React import required (React 17+)
|
|
60
|
+
'react/jsx-uses-react': 'off',
|
|
61
|
+
'react/react-in-jsx-scope': 'off',
|
|
62
|
+
// Rule core — JSX hygiene
|
|
63
|
+
'react/jsx-key': ['error', { checkFragmentShorthand: true }],
|
|
64
|
+
'react/jsx-no-leaked-render': 'error', // forbids `{count && <X />}` rendering "0"
|
|
65
|
+
'react/no-array-index-key': 'error', // index keys break diffing
|
|
66
|
+
'react/no-unstable-nested-components': 'error', // new component identity per render
|
|
67
|
+
'react/self-closing-comp': 'error',
|
|
68
|
+
'react/jsx-pascal-case': 'error',
|
|
69
|
+
'react/jsx-no-useless-fragment': 'error',
|
|
70
|
+
'react/display-name': 'error',
|
|
71
|
+
// ── Hooks (Rules 4.1–4.6, 14.1) ────────────────────────────────────────
|
|
72
|
+
// Rule 14.1 — hooks rules (conditional calls, top-level only)
|
|
73
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
74
|
+
// Rule 4.6 — exhaustive deps (disabling this rule requires DEVIATION(4.6))
|
|
75
|
+
'react-hooks/exhaustive-deps': 'error',
|
|
76
|
+
// ── Props (Rules 2.1–2.4) ───────────────────────────────────────────────
|
|
77
|
+
// Rule 2.4 — no unrestricted rest-prop spread onto DOM elements
|
|
78
|
+
// Note: enforced structurally by Rule 2.1 (explicit Props types); no ESLint rule maps
|
|
79
|
+
// cleanly here without excessive false positives. Code review is the gate.
|
|
80
|
+
// ── Accessibility (Rule 16) ─────────────────────────────────────────────
|
|
81
|
+
// Rule 16.1 — no div/span with onClick; use semantic elements
|
|
82
|
+
'jsx-a11y/click-events-have-key-events': 'error',
|
|
83
|
+
'jsx-a11y/no-static-element-interactions': 'error',
|
|
84
|
+
// Rule 16.2 — accessible forms and images
|
|
85
|
+
'jsx-a11y/alt-text': 'error',
|
|
86
|
+
'jsx-a11y/anchor-has-content': 'error',
|
|
87
|
+
'jsx-a11y/label-has-associated-control': 'error',
|
|
88
|
+
// Rule 16 — ARIA correctness
|
|
89
|
+
'jsx-a11y/aria-props': 'error',
|
|
90
|
+
'jsx-a11y/aria-role': 'error',
|
|
91
|
+
// Autofocus is harmful to keyboard navigation (warn to allow deliberate UX decisions)
|
|
92
|
+
'jsx-a11y/no-autofocus': 'warn',
|
|
93
|
+
// ── TanStack Query (Rules 7.1–7.2) ─────────────────────────────────────
|
|
94
|
+
// Rule 7.2 — exhaustive query-key dependencies
|
|
95
|
+
'@tanstack/query/exhaustive-deps': 'error',
|
|
96
|
+
// Discourage rest-destructuring of query results (encourages explicit field selection)
|
|
97
|
+
'@tanstack/query/no-rest-destructuring': 'error',
|
|
98
|
+
// Stable query-client reference (no inline `new QueryClient()` in render)
|
|
99
|
+
'@tanstack/query/stable-query-client': 'error',
|
|
100
|
+
// ── React-specific functional overrides ─────────────────────────────────
|
|
101
|
+
// React refs are mutable by contract; immutable-data would false-positive on ref.current
|
|
102
|
+
// The base config already allowlists `**.current` via ignoreAccessorPattern — no override needed.
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
// ── React hooks in plain .ts files (custom hooks outside .tsx) ───────────────
|
|
106
|
+
{
|
|
107
|
+
files: ['**/use*.ts', '**/use*.tsx'],
|
|
108
|
+
rules: {
|
|
109
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
110
|
+
'react-hooks/exhaustive-deps': 'error',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
export default config;
|
|
115
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../react.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,WAAW,MAAa,qBAAqB,CAAA;AACpD,OAAO,gBAAgB,MAAQ,2BAA2B,CAAA;AAC1D,wEAAwE;AACxE,OAAO,aAAa,MAAW,wBAAwB,CAAA;AACvD,OAAO,mBAAmB,MAAM,+BAA+B,CAAA;AAC/D,OAAO,IAAI,MAAoB,WAAW,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAQ,oBAAoB,CAAA;AAEnD,MAAM,MAAM,GAAoB;IAE9B,gFAAgF;IAChF,GAAG,IAAI;IAEP,gFAAgF;IAChF;QACE,KAAK,EAAE,CAAC,UAAU,CAAC;QACnB,OAAO,EAAE;YACP,OAAO,EAAc,YAAY,CAAC,WAAW,CAAC;YAC9C,aAAa,EAAQ,YAAY,CAAC,gBAAgB,CAAC;YACnD,UAAU,EAAW,YAAY,CAAC,aAAa,CAAC;YAChD,iBAAiB,EAAI,YAAY,CAAC,mBAAmB,CAAC;SACvD;QACD,QAAQ,EAAE;YACR,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;SAC7B;QACD,KAAK,EAAE;YAEL,0EAA0E;YAE1E,2EAA2E;YAC3E,4FAA4F;YAC5F,+CAA+C;YAC/C,qCAAqC,EAAE,CAAC,OAAO,EAAE;oBAC/C,eAAe,EAAK,gBAAgB;oBACpC,iBAAiB,EAAG,gBAAgB;iBACrC,CAAC;YAEF,gEAAgE;YAChE,sBAAsB,EAAO,KAAK;YAClC,0BAA0B,EAAG,KAAK;YAElC,0BAA0B;YAC1B,eAAe,EAAqB,CAAC,OAAO,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC;YAC/E,4BAA4B,EAAQ,OAAO,EAAI,2CAA2C;YAC1F,0BAA0B,EAAU,OAAO,EAAI,2BAA2B;YAC1E,qCAAqC,EAAE,OAAO,EAAE,oCAAoC;YACpF,yBAAyB,EAAW,OAAO;YAC3C,uBAAuB,EAAa,OAAO;YAC3C,+BAA+B,EAAK,OAAO;YAC3C,oBAAoB,EAAgB,OAAO;YAE3C,0EAA0E;YAE1E,8DAA8D;YAC9D,4BAA4B,EAAI,OAAO;YAEvC,2EAA2E;YAC3E,6BAA6B,EAAG,OAAO;YAEvC,2EAA2E;YAE3E,gEAAgE;YAChE,sFAAsF;YACtF,2EAA2E;YAE3E,2EAA2E;YAE3E,8DAA8D;YAC9D,uCAAuC,EAAG,OAAO;YACjD,yCAAyC,EAAE,OAAO;YAElD,0CAA0C;YAC1C,mBAAmB,EAAoB,OAAO;YAC9C,6BAA6B,EAAU,OAAO;YAC9C,uCAAuC,EAAE,OAAO;YAEhD,6BAA6B;YAC7B,qBAAqB,EAAG,OAAO;YAC/B,oBAAoB,EAAI,OAAO;YAE/B,sFAAsF;YACtF,uBAAuB,EAAE,MAAM;YAE/B,0EAA0E;YAE1E,+CAA+C;YAC/C,iCAAiC,EAAK,OAAO;YAE7C,uFAAuF;YACvF,uCAAuC,EAAE,OAAO;YAEhD,0EAA0E;YAC1E,qCAAqC,EAAE,OAAO;YAE9C,2EAA2E;YAE3E,yFAAyF;YACzF,kGAAkG;SACnG;KACF;IAED,gFAAgF;IAChF;QACE,KAAK,EAAE,CAAC,YAAY,EAAE,aAAa,CAAC;QACpC,KAAK,EAAE;YACL,4BAA4B,EAAG,OAAO;YACtC,6BAA6B,EAAE,OAAO;SACvC;KACF;CACF,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tsfpp/eslint-config",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared ESLint flat configuration for TSF++ projects",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"eslint",
|
|
7
|
+
"eslint-config",
|
|
8
|
+
"typescript",
|
|
9
|
+
"tsfpp",
|
|
10
|
+
"functional",
|
|
11
|
+
"immutability"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/tsfpp/tsfpp#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/tsfpp/tsfpp.git",
|
|
17
|
+
"directory": "packages/eslint-config"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./dist/base.js",
|
|
23
|
+
"./react": "./dist/react.js",
|
|
24
|
+
"./api": "./dist/api.js",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc --project tsconfig.build.json",
|
|
33
|
+
"prepublishOnly": "pnpm run build",
|
|
34
|
+
"lint": "eslint dist/",
|
|
35
|
+
"test": "node --input-type=module --eval \"import './dist/base.js'\""
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.18.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@tanstack/eslint-plugin-query": ">=5.0.0",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": ">=8.0.0",
|
|
43
|
+
"@typescript-eslint/parser": ">=8.0.0",
|
|
44
|
+
"eslint": ">=9.0.0",
|
|
45
|
+
"eslint-plugin-functional": "^9.0.0",
|
|
46
|
+
"eslint-plugin-jsx-a11y": ">=6.0.0",
|
|
47
|
+
"eslint-plugin-react": ">=7.0.0",
|
|
48
|
+
"eslint-plugin-react-hooks": ">=5.0.0",
|
|
49
|
+
"typescript": ">=5.3.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"@tanstack/eslint-plugin-query": { "optional": true },
|
|
53
|
+
"eslint-plugin-jsx-a11y": { "optional": true },
|
|
54
|
+
"eslint-plugin-react": { "optional": true },
|
|
55
|
+
"eslint-plugin-react-hooks": { "optional": true }
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@tanstack/eslint-plugin-query": "^5.100.10",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.59.3",
|
|
60
|
+
"@typescript-eslint/parser": "^8.59.3",
|
|
61
|
+
"eslint": "^9.39.4",
|
|
62
|
+
"eslint-plugin-functional": "^9.0.0",
|
|
63
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
64
|
+
"eslint-plugin-react": "^7.37.5",
|
|
65
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
67
|
+
}
|
|
68
|
+
}
|