Skip to content

Commit ccb090a

Browse files
authored
Create eslint.effect-ts-check.config.mjs
1 parent dcc7462 commit ccb090a

1 file changed

Lines changed: 220 additions & 0 deletions

File tree

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// CHANGE: add Effect-TS compliance lint profile
2+
// WHY: detect current deviations from strict Effect-TS guidance
3+
// QUOTE(TZ): n/a
4+
// REF: AGENTS.md Effect-TS compliance checks
5+
// SOURCE: n/a
6+
// PURITY: SHELL
7+
// EFFECT: eslint config
8+
// INVARIANT: config only flags explicit policy deviations
9+
// COMPLEXITY: O(1)/O(1)
10+
import eslintComments from "@eslint-community/eslint-plugin-eslint-comments"
11+
import globals from "globals"
12+
import tseslint from "typescript-eslint"
13+
14+
const restrictedImports = [
15+
{
16+
name: "node:fs",
17+
message: "Use @effect/platform FileSystem instead of node:fs."
18+
},
19+
{
20+
name: "fs",
21+
message: "Use @effect/platform FileSystem instead of fs."
22+
},
23+
{
24+
name: "node:fs/promises",
25+
message: "Use @effect/platform FileSystem instead of node:fs/promises."
26+
},
27+
{
28+
name: "node:path/posix",
29+
message: "Use @effect/platform Path instead of node:path/posix."
30+
},
31+
{
32+
name: "node:path",
33+
message: "Use @effect/platform Path instead of node:path."
34+
},
35+
{
36+
name: "path",
37+
message: "Use @effect/platform Path instead of path."
38+
},
39+
{
40+
name: "node:child_process",
41+
message: "Use @effect/platform Command instead of node:child_process."
42+
},
43+
{
44+
name: "child_process",
45+
message: "Use @effect/platform Command instead of child_process."
46+
},
47+
{
48+
name: "node:process",
49+
message: "Use @effect/platform Runtime instead of node:process."
50+
},
51+
{
52+
name: "process",
53+
message: "Use @effect/platform Runtime instead of process."
54+
}
55+
]
56+
57+
const restrictedSyntaxBase = [
58+
{
59+
selector: "SwitchStatement",
60+
message: "Switch is forbidden. Use Match.exhaustive."
61+
},
62+
{
63+
selector: "TryStatement",
64+
message: "Avoid try/catch in product code. Use Effect.try / Effect.catch*."
65+
},
66+
{
67+
selector: "AwaitExpression",
68+
message: "Avoid await. Use Effect.gen / Effect.flatMap."
69+
},
70+
{
71+
selector: "FunctionDeclaration[async=true], FunctionExpression[async=true], ArrowFunctionExpression[async=true]",
72+
message: "Avoid async/await. Use Effect.gen / Effect.tryPromise."
73+
},
74+
{
75+
selector: "NewExpression[callee.name='Promise']",
76+
message: "Avoid new Promise. Use Effect.async / Effect.tryPromise."
77+
},
78+
{
79+
selector: "CallExpression[callee.object.name='Promise']",
80+
message: "Avoid Promise.*. Use Effect combinators."
81+
},
82+
{
83+
selector: "CallExpression[callee.name='require']",
84+
message: "Avoid require(). Use ES module imports."
85+
},
86+
{
87+
selector: "TSAsExpression",
88+
message: "Casting is only allowed in src/core/axioms.ts."
89+
},
90+
{
91+
selector: "TSTypeAssertion",
92+
message: "Casting is only allowed in src/core/axioms.ts."
93+
},
94+
{
95+
selector: "CallExpression[callee.name='makeFilesystemService']",
96+
message: "Do not instantiate FilesystemService directly. Provide Layer and access via Tag."
97+
},
98+
{
99+
selector: "CallExpression[callee.property.name='catchAll']",
100+
message: "Avoid catchAll that discards typed errors; map or propagate explicitly."
101+
}
102+
]
103+
104+
const restrictedSyntaxCore = [
105+
...restrictedSyntaxBase,
106+
{
107+
selector: "TSUnknownKeyword",
108+
message: "unknown is allowed only at shell boundaries with decoding."
109+
},
110+
{
111+
selector: "CallExpression[callee.property.name='runSyncExit']",
112+
message: "Effect.runSyncExit is shell-only. Move to a runner."
113+
},
114+
{
115+
selector: "CallExpression[callee.property.name='runSync']",
116+
message: "Effect.runSync is shell-only. Move to a runner."
117+
},
118+
{
119+
selector: "CallExpression[callee.property.name='runPromise']",
120+
message: "Effect.runPromise is shell-only. Move to a runner."
121+
}
122+
]
123+
124+
const restrictedSyntaxCoreNoAs = [
125+
...restrictedSyntaxCore.filter((rule) =>
126+
rule.selector !== "TSAsExpression" && rule.selector !== "TSTypeAssertion"
127+
)
128+
]
129+
130+
const restrictedSyntaxBaseNoServiceFactory = [
131+
...restrictedSyntaxBase.filter((rule) =>
132+
rule.selector !== "CallExpression[callee.name='makeFilesystemService']"
133+
)
134+
]
135+
136+
export default tseslint.config(
137+
{
138+
name: "effect-ts-compliance-check",
139+
files: ["src/**/*.ts", "scripts/**/*.ts"],
140+
languageOptions: {
141+
parser: tseslint.parser,
142+
globals: { ...globals.node }
143+
},
144+
plugins: {
145+
"@typescript-eslint": tseslint.plugin,
146+
"eslint-comments": eslintComments
147+
},
148+
rules: {
149+
"no-console": "error",
150+
"no-restricted-imports": ["error", {
151+
paths: restrictedImports,
152+
patterns: [
153+
{
154+
group: ["node:*"],
155+
message: "Do not import from node:* directly. Use @effect/platform-node or @effect/platform services."
156+
}
157+
]
158+
}],
159+
"no-restricted-syntax": ["error", ...restrictedSyntaxBase],
160+
"@typescript-eslint/no-explicit-any": "error",
161+
"@typescript-eslint/ban-ts-comment": ["error", {
162+
"ts-ignore": true,
163+
"ts-nocheck": true,
164+
"ts-check": false,
165+
"ts-expect-error": true
166+
}],
167+
"@typescript-eslint/no-restricted-types": ["error", {
168+
types: {
169+
Promise: {
170+
message: "Avoid Promise in types. Use Effect.Effect<A, E, R>."
171+
},
172+
"Promise<*>": {
173+
message: "Avoid Promise<T>. Use Effect.Effect<T, E, R>."
174+
}
175+
}
176+
}],
177+
"eslint-comments/no-use": "error",
178+
"eslint-comments/no-unlimited-disable": "error",
179+
"eslint-comments/disable-enable-pair": "error",
180+
"eslint-comments/no-unused-disable": "error"
181+
}
182+
},
183+
{
184+
name: "effect-ts-compliance-core",
185+
files: ["src/core/**/*.ts"],
186+
rules: {
187+
"no-restricted-syntax": ["error", ...restrictedSyntaxCore],
188+
"no-restricted-imports": ["error", {
189+
paths: restrictedImports,
190+
patterns: [
191+
{
192+
group: [
193+
"../shell/**",
194+
"../../shell/**",
195+
"../../../shell/**",
196+
"./shell/**",
197+
"src/shell/**",
198+
"shell/**"
199+
],
200+
message: "CORE must not import from SHELL."
201+
}
202+
]
203+
}]
204+
}
205+
},
206+
{
207+
name: "effect-ts-compliance-axioms",
208+
files: ["src/core/axioms.ts"],
209+
rules: {
210+
"no-restricted-syntax": ["error", ...restrictedSyntaxCoreNoAs]
211+
}
212+
},
213+
{
214+
name: "effect-ts-compliance-filesystem-service",
215+
files: ["src/shell/services/filesystem.ts"],
216+
rules: {
217+
"no-restricted-syntax": ["error", ...restrictedSyntaxBaseNoServiceFactory]
218+
}
219+
}
220+
)

0 commit comments

Comments
 (0)