@tsfpp/eslint-config 1.0.2 → 1.1.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/dist/layered.d.ts +35 -0
- package/dist/layered.d.ts.map +1 -0
- package/dist/layered.js +115 -0
- package/dist/layered.js.map +1 -0
- package/dist/plugin-compat.d.ts.map +1 -1
- package/dist/plugin-compat.js +3 -2
- package/dist/plugin-compat.js.map +1 -1
- package/package.json +31 -22
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config/layered
|
|
3
|
+
*
|
|
4
|
+
* TSF++ ESLint profile enforcing Rule 11.5 (layer boundaries — functional core,
|
|
5
|
+
* imperative shell) and Rule 11.6 (import purity).
|
|
6
|
+
*
|
|
7
|
+
* Rule references in this file are to CODING_STANDARD.md.
|
|
8
|
+
*
|
|
9
|
+
* Why a separate profile: the base config constrains how a *file* is written.
|
|
10
|
+
* These rules constrain how files may *depend on each other*, which is what
|
|
11
|
+
* actually keeps the axioms true at program scale — a single outward import
|
|
12
|
+
* makes every function in a module effectful no matter how each was written.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* // eslint.config.js
|
|
16
|
+
* import tsfppLayered from '@tsfpp/eslint-config/layered'
|
|
17
|
+
* export default tsfppLayered
|
|
18
|
+
*
|
|
19
|
+
* The path globs below assume the conventional layout:
|
|
20
|
+
*
|
|
21
|
+
* src/core/ pure domain — types, ADTs, decisions
|
|
22
|
+
* src/use-case/ orchestration + ports
|
|
23
|
+
* src/boundary/ parsing and error mapping
|
|
24
|
+
* src/adapters/ effects: db, http clients, clock, entropy
|
|
25
|
+
* src/handlers/ transport entry points
|
|
26
|
+
* src/main.ts composition root
|
|
27
|
+
*
|
|
28
|
+
* Layer *names* are conventional; the inward-only *direction* is normative
|
|
29
|
+
* (Rule 11.5). Projects using a different layout should copy this file and
|
|
30
|
+
* adjust the globs — the shape of the restriction is the part worth keeping.
|
|
31
|
+
*/
|
|
32
|
+
import type { Linter } from 'eslint';
|
|
33
|
+
declare const config: Linter.Config[];
|
|
34
|
+
export default config;
|
|
35
|
+
//# sourceMappingURL=layered.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layered.d.ts","sourceRoot":"","sources":["../layered.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AA6BpC,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EA4D1B,CAAA;AAED,eAAe,MAAM,CAAA"}
|
package/dist/layered.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tsfpp/eslint-config/layered
|
|
3
|
+
*
|
|
4
|
+
* TSF++ ESLint profile enforcing Rule 11.5 (layer boundaries — functional core,
|
|
5
|
+
* imperative shell) and Rule 11.6 (import purity).
|
|
6
|
+
*
|
|
7
|
+
* Rule references in this file are to CODING_STANDARD.md.
|
|
8
|
+
*
|
|
9
|
+
* Why a separate profile: the base config constrains how a *file* is written.
|
|
10
|
+
* These rules constrain how files may *depend on each other*, which is what
|
|
11
|
+
* actually keeps the axioms true at program scale — a single outward import
|
|
12
|
+
* makes every function in a module effectful no matter how each was written.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* // eslint.config.js
|
|
16
|
+
* import tsfppLayered from '@tsfpp/eslint-config/layered'
|
|
17
|
+
* export default tsfppLayered
|
|
18
|
+
*
|
|
19
|
+
* The path globs below assume the conventional layout:
|
|
20
|
+
*
|
|
21
|
+
* src/core/ pure domain — types, ADTs, decisions
|
|
22
|
+
* src/use-case/ orchestration + ports
|
|
23
|
+
* src/boundary/ parsing and error mapping
|
|
24
|
+
* src/adapters/ effects: db, http clients, clock, entropy
|
|
25
|
+
* src/handlers/ transport entry points
|
|
26
|
+
* src/main.ts composition root
|
|
27
|
+
*
|
|
28
|
+
* Layer *names* are conventional; the inward-only *direction* is normative
|
|
29
|
+
* (Rule 11.5). Projects using a different layout should copy this file and
|
|
30
|
+
* adjust the globs — the shape of the restriction is the part worth keeping.
|
|
31
|
+
*/
|
|
32
|
+
/** Modules a pure core may never reach for. */
|
|
33
|
+
const OUTWARD_FROM_CORE = [
|
|
34
|
+
'**/adapters/**',
|
|
35
|
+
'**/infra/**',
|
|
36
|
+
'**/handlers/**',
|
|
37
|
+
'**/boundary/**',
|
|
38
|
+
'**/use-case/**',
|
|
39
|
+
'node:*',
|
|
40
|
+
'express',
|
|
41
|
+
'fastify',
|
|
42
|
+
'koa',
|
|
43
|
+
'next/*',
|
|
44
|
+
'react',
|
|
45
|
+
'react-dom',
|
|
46
|
+
];
|
|
47
|
+
/** Modules a use case may never reach for (it may use the core). */
|
|
48
|
+
const OUTWARD_FROM_USE_CASE = [
|
|
49
|
+
'**/adapters/**',
|
|
50
|
+
'**/infra/**',
|
|
51
|
+
'**/handlers/**',
|
|
52
|
+
'node:*',
|
|
53
|
+
'express',
|
|
54
|
+
'fastify',
|
|
55
|
+
'koa',
|
|
56
|
+
];
|
|
57
|
+
const config = [
|
|
58
|
+
// ── Rule 11.5 — the core is pure: nothing peripheral, nothing transport-shaped
|
|
59
|
+
{
|
|
60
|
+
files: ['**/core/**/*.ts', '**/domain/**/*.ts'],
|
|
61
|
+
rules: {
|
|
62
|
+
'no-restricted-imports': ['error', {
|
|
63
|
+
patterns: [{
|
|
64
|
+
group: OUTWARD_FROM_CORE,
|
|
65
|
+
message: 'TSF++ Rule 11.5: the core must not import adapters, transports, or node builtins. ' +
|
|
66
|
+
'Define a port (a function type) in the core and inject an implementation at the composition root (Rule 6.5).',
|
|
67
|
+
}],
|
|
68
|
+
}],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
// ── Rule 11.5 — use cases orchestrate the core; they do not perform effects
|
|
72
|
+
{
|
|
73
|
+
files: ['**/use-case/**/*.ts', '**/use-cases/**/*.ts', '**/application/**/*.ts'],
|
|
74
|
+
rules: {
|
|
75
|
+
'no-restricted-imports': ['error', {
|
|
76
|
+
patterns: [{
|
|
77
|
+
group: OUTWARD_FROM_USE_CASE,
|
|
78
|
+
message: 'TSF++ Rule 11.5: a use case may depend on the core and on ports, never on a concrete adapter. ' +
|
|
79
|
+
'Accept the port through Deps (Rule 6.5).',
|
|
80
|
+
}],
|
|
81
|
+
}],
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
// ── Rule 11.6 — import purity: a module may only define bindings
|
|
85
|
+
//
|
|
86
|
+
// Top-level ambient reads are worse than the in-function reads Rule 4.6
|
|
87
|
+
// forbids: they run before any caller exists, in module-graph order, and can
|
|
88
|
+
// be neither injected nor stubbed nor deferred.
|
|
89
|
+
{
|
|
90
|
+
files: ['**/*.ts'],
|
|
91
|
+
ignores: ['**/main.ts', '**/index.ts', '**/*.config.ts', '**/*.test.ts'],
|
|
92
|
+
rules: {
|
|
93
|
+
'no-restricted-syntax': ['error',
|
|
94
|
+
{
|
|
95
|
+
selector: ":matches(Program > VariableDeclaration, Program > ExportNamedDeclaration > VariableDeclaration) > VariableDeclarator > NewExpression[callee.name='Date']",
|
|
96
|
+
message: 'TSF++ Rule 11.6: reading the clock at import time. Expose a function and inject the clock (Rule 4.6).',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
selector: ":matches(Program > VariableDeclaration, Program > ExportNamedDeclaration > VariableDeclaration) > VariableDeclarator > MemberExpression[object.object.name='process'][object.property.name='env']",
|
|
100
|
+
message: 'TSF++ Rule 11.6: reading process.env at import time. Load config at the boundary and inject it.',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
selector: ":matches(Program > VariableDeclaration, Program > ExportNamedDeclaration > VariableDeclaration) > VariableDeclarator > AwaitExpression",
|
|
104
|
+
message: 'TSF++ Rule 11.6: top-level await performs I/O at import time and blocks the module graph.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
selector: 'Program > ExpressionStatement > CallExpression',
|
|
108
|
+
message: 'TSF++ Rule 11.6: a bare call at module scope is a side effect at import time. Importing a module must only define bindings.',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
export default config;
|
|
115
|
+
//# sourceMappingURL=layered.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layered.js","sourceRoot":"","sources":["../layered.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,+CAA+C;AAC/C,MAAM,iBAAiB,GAAG;IACxB,gBAAgB;IAChB,aAAa;IACb,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,QAAQ;IACR,SAAS;IACT,SAAS;IACT,KAAK;IACL,QAAQ;IACR,OAAO;IACP,WAAW;CACZ,CAAA;AAED,oEAAoE;AACpE,MAAM,qBAAqB,GAAG;IAC5B,gBAAgB;IAChB,aAAa;IACb,gBAAgB;IAChB,QAAQ;IACR,SAAS;IACT,SAAS;IACT,KAAK;CACN,CAAA;AAED,MAAM,MAAM,GAAoB;IAC9B,gFAAgF;IAChF;QACE,KAAK,EAAE,CAAC,iBAAiB,EAAE,mBAAmB,CAAC;QAC/C,KAAK,EAAE;YACL,uBAAuB,EAAE,CAAC,OAAO,EAAE;oBACjC,QAAQ,EAAE,CAAC;4BACT,KAAK,EAAE,iBAAiB;4BACxB,OAAO,EACL,oFAAoF;gCACpF,8GAA8G;yBACjH,CAAC;iBACH,CAAC;SACH;KACF;IAED,6EAA6E;IAC7E;QACE,KAAK,EAAE,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,wBAAwB,CAAC;QAChF,KAAK,EAAE;YACL,uBAAuB,EAAE,CAAC,OAAO,EAAE;oBACjC,QAAQ,EAAE,CAAC;4BACT,KAAK,EAAE,qBAAqB;4BAC5B,OAAO,EACL,gGAAgG;gCAChG,0CAA0C;yBAC7C,CAAC;iBACH,CAAC;SACH;KACF;IAED,kEAAkE;IAClE,EAAE;IACF,wEAAwE;IACxE,6EAA6E;IAC7E,gDAAgD;IAChD;QACE,KAAK,EAAE,CAAC,SAAS,CAAC;QAClB,OAAO,EAAE,CAAC,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,CAAC;QACxE,KAAK,EAAE;YACL,sBAAsB,EAAE,CAAC,OAAO;gBAC9B;oBACE,QAAQ,EAAE,0JAA0J;oBACpK,OAAO,EAAE,uGAAuG;iBACjH;gBACD;oBACE,QAAQ,EAAE,mMAAmM;oBAC7M,OAAO,EAAE,iGAAiG;iBAC3G;gBACD;oBACE,QAAQ,EAAE,wIAAwI;oBAClJ,OAAO,EAAE,2FAA2F;iBACrG;gBACD;oBACE,QAAQ,EAAE,gDAAgD;oBAC1D,OAAO,EAAE,6HAA6H;iBACvI;aACF;SACF;KACF;CACF,CAAA;AAED,eAAe,MAAM,CAAA"}
|
|
@@ -1 +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;
|
|
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;AACH,eAAO,MAAM,YAAY,GAAI,QAAQ,OAAO,KAAG,MAAM,CAAC,MAE7B,CAAA"}
|
package/dist/plugin-compat.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* Use exclusively at plugin registration sites. Never use to silence unrelated
|
|
26
26
|
* type errors.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
export const coercePlugin = (plugin) =>
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- DEVIATION(1.6): see module JSDoc above
|
|
30
|
+
plugin;
|
|
30
31
|
//# sourceMappingURL=plugin-compat.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-compat.js","sourceRoot":"","sources":["../plugin-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;GAMG;AACH,
|
|
1
|
+
{"version":3,"file":"plugin-compat.js","sourceRoot":"","sources":["../plugin-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAe,EAAiB,EAAE;AAC7D,mHAAmH;AACnH,MAAuB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsfpp/eslint-config",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Shared ESLint flat configuration for TSF++ projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -13,26 +13,27 @@
|
|
|
13
13
|
"homepage": "https://github.com/tsfpp/eslint-config#readme",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "https://github.com/tsfpp/eslint-config.git",
|
|
16
|
+
"url": "git+https://github.com/tsfpp/eslint-config.git",
|
|
17
17
|
"directory": "packages/eslint-config"
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"exports": {
|
|
22
|
-
".":
|
|
23
|
-
"./react":
|
|
24
|
-
"./api":
|
|
25
|
-
"./package.json": "./package.json"
|
|
22
|
+
".": "./dist/base.js",
|
|
23
|
+
"./react": "./dist/react.js",
|
|
24
|
+
"./api": "./dist/api.js",
|
|
25
|
+
"./package.json": "./package.json",
|
|
26
|
+
"./layered": "./dist/layered.js"
|
|
26
27
|
},
|
|
27
28
|
"files": [
|
|
28
29
|
"dist/",
|
|
29
30
|
"README.md"
|
|
30
31
|
],
|
|
31
32
|
"scripts": {
|
|
32
|
-
"build":
|
|
33
|
+
"build": "tsc --project tsconfig.build.json",
|
|
33
34
|
"prepublishOnly": "pnpm run build",
|
|
34
|
-
"lint":
|
|
35
|
-
"test":
|
|
35
|
+
"lint": "eslint .",
|
|
36
|
+
"test": "node --input-type=module --eval \"import './dist/base.js'\""
|
|
36
37
|
},
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=18.18.0"
|
|
@@ -49,20 +50,28 @@
|
|
|
49
50
|
"typescript": ">=5.3.0"
|
|
50
51
|
},
|
|
51
52
|
"peerDependenciesMeta": {
|
|
52
|
-
"@tanstack/eslint-plugin-query": {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"eslint-plugin-
|
|
53
|
+
"@tanstack/eslint-plugin-query": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"eslint-plugin-jsx-a11y": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"eslint-plugin-react": {
|
|
60
|
+
"optional": true
|
|
61
|
+
},
|
|
62
|
+
"eslint-plugin-react-hooks": {
|
|
63
|
+
"optional": true
|
|
64
|
+
}
|
|
56
65
|
},
|
|
57
66
|
"devDependencies": {
|
|
58
|
-
"@tanstack/eslint-plugin-query":
|
|
67
|
+
"@tanstack/eslint-plugin-query": "^5.100.10",
|
|
59
68
|
"@typescript-eslint/eslint-plugin": "^8.59.3",
|
|
60
|
-
"@typescript-eslint/parser":
|
|
61
|
-
"eslint":
|
|
62
|
-
"eslint-plugin-functional":
|
|
63
|
-
"eslint-plugin-jsx-a11y":
|
|
64
|
-
"eslint-plugin-react":
|
|
65
|
-
"eslint-plugin-react-hooks":
|
|
66
|
-
"typescript":
|
|
69
|
+
"@typescript-eslint/parser": "^8.59.3",
|
|
70
|
+
"eslint": "^9.39.4",
|
|
71
|
+
"eslint-plugin-functional": "^9.0.0",
|
|
72
|
+
"eslint-plugin-jsx-a11y": "^6.10.2",
|
|
73
|
+
"eslint-plugin-react": "^7.37.5",
|
|
74
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
75
|
+
"typescript": "^5.9.3"
|
|
67
76
|
}
|
|
68
|
-
}
|
|
77
|
+
}
|