oxlint-plugin-effect 0.0.0 → 0.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/CHANGELOG.md +37 -0
- package/package.json +6 -7
- package/src/presets/core.ts +23 -12
- package/src/presets/effect-native.ts +18 -25
- package/src/presets/full.ts +32 -46
- package/src/presets/functional.ts +18 -0
- package/src/presets/index.ts +2 -0
- package/src/presets/strict.ts +77 -0
- package/src/rules/index.ts +17 -28
- package/src/rules/no-arrow-ladder.ts +1 -2
- package/src/rules/no-async-function.ts +1 -2
- package/src/rules/no-catch-all-to-map-error.ts +89 -0
- package/src/rules/no-effect-fn-generator.ts +1 -2
- package/src/rules/no-effect-gen-adapter.ts +1 -2
- package/src/rules/no-effect-map-flatten.ts +82 -0
- package/src/rules/no-effect-map-void.ts +1 -2
- package/src/rules/no-effect-orElse-ladder.ts +1 -2
- package/src/rules/no-effect-succeed-string.ts +1 -2
- package/src/rules/no-effect-succeed-variable.ts +1 -2
- package/src/rules/no-effect-succeed-void.ts +1 -2
- package/src/rules/no-effect-sync-console.ts +1 -2
- package/src/rules/no-effect-sync-wrapper.ts +1 -2
- package/src/rules/no-effect-type-alias.ts +1 -2
- package/src/rules/no-effect-wrapper-alias.ts +1 -4
- package/src/rules/no-extends-native-error.ts +1 -2
- package/src/rules/no-flatmap-ladder.ts +1 -3
- package/src/rules/no-fromnullable-coalesce.ts +1 -2
- package/src/rules/no-global-error-in-catch.ts +104 -0
- package/src/rules/no-global-error-in-failure.ts +83 -0
- package/src/rules/no-globals.ts +178 -0
- package/src/rules/no-iife-wrapper.ts +1 -2
- package/src/rules/{no-inline-runtime-provide.ts → no-inline-provide.ts} +3 -4
- package/src/rules/no-instanceof-schema.ts +2 -3
- package/src/rules/no-manual-effect-channels.ts +1 -2
- package/src/rules/no-match-effect-branch.ts +1 -2
- package/src/rules/no-match-void-branch.ts +1 -2
- package/src/rules/no-multiple-effect-provide.ts +75 -0
- package/src/rules/no-nested-effect-call.ts +1 -3
- package/src/rules/no-nested-effect-gen.ts +1 -2
- package/src/rules/no-nested-pipe.ts +1 -4
- package/src/rules/no-new-error.ts +46 -10
- package/src/rules/no-new-promise.ts +43 -7
- package/src/rules/no-node-builtin-import.ts +1 -3
- package/src/rules/no-option-boolean-normalization.ts +1 -2
- package/src/rules/no-positional-log-error.ts +76 -0
- package/src/rules/no-redundant-schema-tag-identifier.ts +62 -0
- package/src/rules/no-return-in-arrow.ts +1 -2
- package/src/rules/no-return-nullish.ts +80 -0
- package/src/rules/no-schema-struct-with-tag.ts +64 -0
- package/src/rules/no-schema-union-of-literals.ts +48 -0
- package/src/rules/no-string-sentinel-const.ts +1 -2
- package/src/rules/no-ternary.ts +1 -3
- package/src/rules/no-throw-statement.ts +32 -6
- package/src/rules/no-try-catch.ts +32 -7
- package/src/rules/no-unnecessary-arrow-block.ts +1 -2
- package/src/rules/no-unnecessary-effect-gen.ts +1 -3
- package/src/rules/no-unnecessary-pipe-chain.ts +47 -0
- package/src/rules/no-unnecessary-pipe.ts +1 -2
- package/src/vendor/effect-oxlint/Rule.ts +111 -173
- package/tests/ban-rules.test.ts +0 -167
- package/tests/context-rules.test.ts +75 -158
- package/tests/pattern-rules.test.ts +39 -50
- package/src/rules/no-console-in-effect.ts +0 -46
- package/src/rules/no-date-in-effect.ts +0 -61
- package/src/rules/no-dynamic-import.ts +0 -13
- package/src/rules/no-fetch-in-effect.ts +0 -47
- package/src/rules/no-global-console.ts +0 -17
- package/src/rules/no-global-date.ts +0 -20
- package/src/rules/no-global-fetch.ts +0 -14
- package/src/rules/no-global-random.ts +0 -13
- package/src/rules/no-global-timers.ts +0 -17
- package/src/rules/no-json-in-effect.ts +0 -45
- package/src/rules/no-json-parse.ts +0 -18
- package/src/rules/no-platform-globals.ts +0 -117
- package/src/rules/no-process-env-in-effect.ts +0 -39
- package/src/rules/no-process-env.ts +0 -13
- package/src/rules/no-random-in-effect.ts +0 -39
- package/src/rules/no-return-null.ts +0 -47
- package/src/rules/no-throw-in-effect-gen.ts +0 -42
- package/src/rules/no-timers-in-effect.ts +0 -47
- package/src/rules/no-try-catch-in-effect-gen.ts +0 -42
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# oxlint-plugin-effect
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`e1c49ed`](https://github.com/cevr/effect-oxlint/commit/e1c49ed4195de9df24c0e30196285ec29cfe5748) Thanks [@cevr](https://github.com/cevr)! - Major rule overhaul: add 16 Go type-aware rules, consolidate JS rules, enforce Effect model.
|
|
8
|
+
|
|
9
|
+
**JS plugin (59 rules, was 66):**
|
|
10
|
+
|
|
11
|
+
New rules:
|
|
12
|
+
|
|
13
|
+
- noCatchAllToMapError, noUnnecessaryPipeChain, noMultipleEffectProvide
|
|
14
|
+
- noSchemaUnionOfLiterals, noSchemaStructWithTag, noRedundantSchemaTagIdentifier
|
|
15
|
+
- noEffectMapFlatten, noGlobalErrorInFailure, noGlobalErrorInCatch
|
|
16
|
+
- noPositionalLogError (from agent session analysis — 57 violations)
|
|
17
|
+
- noReturnNullish (catches null, undefined, void 0 → points to Option.none())
|
|
18
|
+
- noGlobals (consolidated context rule — console, fetch, Date, Math.random, crypto, timers, JSON, process/Bun/Deno with per-API Effect alternative hints)
|
|
19
|
+
|
|
20
|
+
Removed 14 generic global bans (no-console, no-date, no-fetch, etc.) — these belong in oxlint built-in config, not an Effect plugin. Every removed rule has an Effect-context counterpart in noGlobals.
|
|
21
|
+
|
|
22
|
+
Restored 5 Effect-enforcing rules that prevent escaping the Effect model:
|
|
23
|
+
|
|
24
|
+
- noThrowStatement → "Use yield* Effect.fail() or yield* new MyError()"
|
|
25
|
+
- noTryCatch → "Use Effect.try / Effect.tryPromise"
|
|
26
|
+
- noNewPromise → "Use Effect.async / Effect.tryPromise / Effect.promise"
|
|
27
|
+
- noNewError → "Define class MyError extends Schema.TaggedErrorClass..."
|
|
28
|
+
- noReturnNullish → "Use Option.none() / Effect.void"
|
|
29
|
+
|
|
30
|
+
Redesigned Rule.banMultiple API with per-spec BanSpec discriminated union.
|
|
31
|
+
Promoted high-frequency agent violations from warn to error in core/full presets.
|
|
32
|
+
|
|
33
|
+
**Go binary (24 rules, was 8):**
|
|
34
|
+
|
|
35
|
+
- Layer/leak detection: missingEffectContext, missingEffectError, missingLayerContext, leakingRequirements, layerMergeAllWithDependencies
|
|
36
|
+
- Error hygiene: anyUnknownInError, unknownInEffectCatch
|
|
37
|
+
- Code quality: effectFnOpportunity, classSelfMismatch, fnImplicitAny, scopeInLayerEffect, strictProvide, unnecessaryFailYieldable, genericServices, overriddenSchemaConstructor, nonObjectServiceType
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxlint-plugin-effect",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "oxlint plugin with Effect-specific lint rules",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/cevr/effect-oxlint"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": "./src/index.ts",
|
|
10
13
|
"./plugin": "./src/plugin.ts",
|
|
@@ -23,9 +26,7 @@
|
|
|
23
26
|
"test": "bun test",
|
|
24
27
|
"gate": "concurrently -n type,style,test -c blue,yellow,green \"bun run typecheck\" \"bun run lint\" \"bun run test\"",
|
|
25
28
|
"codegen": "bun run scripts/codegen.ts",
|
|
26
|
-
"add-rule": "bun run scripts/add-rule.ts"
|
|
27
|
-
"version": "changeset version",
|
|
28
|
-
"release": "changeset publish"
|
|
29
|
+
"add-rule": "bun run scripts/add-rule.ts"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
32
|
"@oxlint/plugins": "^1.60.0"
|
|
@@ -34,8 +35,6 @@
|
|
|
34
35
|
"effect": ">=4.0.0-beta.49"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
|
-
"@changesets/changelog-github": "^0.6.0",
|
|
38
|
-
"@changesets/cli": "^2.30.0",
|
|
39
38
|
"@effect/language-service": "latest",
|
|
40
39
|
"@effect/vitest": "latest",
|
|
41
40
|
"@types/bun": "latest",
|
package/src/presets/core.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Core preset — essential Effect rules
|
|
2
|
+
* Core preset — essential Effect rules.
|
|
3
3
|
* Good starting point for projects transitioning to Effect.
|
|
4
4
|
*/
|
|
5
5
|
export const core = {
|
|
@@ -13,9 +13,9 @@ export const core = {
|
|
|
13
13
|
// AST pattern rules
|
|
14
14
|
"effect/noNestedPipe": "error",
|
|
15
15
|
"effect/noNestedEffectGen": "error",
|
|
16
|
-
"effect/noUnnecessaryEffectGen": "
|
|
17
|
-
"effect/noEffectSucceedVoid": "
|
|
18
|
-
"effect/noEffectMapVoid": "
|
|
16
|
+
"effect/noUnnecessaryEffectGen": "error",
|
|
17
|
+
"effect/noEffectSucceedVoid": "error",
|
|
18
|
+
"effect/noEffectMapVoid": "error",
|
|
19
19
|
"effect/noUnnecessaryPipe": "warn",
|
|
20
20
|
"effect/noEffectFnGenerator": "warn",
|
|
21
21
|
"effect/noFlatmapLadder": "error",
|
|
@@ -23,14 +23,25 @@ export const core = {
|
|
|
23
23
|
"effect/noIifeWrapper": "warn",
|
|
24
24
|
"effect/noFromNullableCoalesce": "warn",
|
|
25
25
|
"effect/noUnnecessaryArrowBlock": "warn",
|
|
26
|
-
"effect/noEffectGenAdapter": "
|
|
26
|
+
"effect/noEffectGenAdapter": "error",
|
|
27
27
|
"effect/noEffectSyncConsole": "error",
|
|
28
|
-
"effect/
|
|
28
|
+
"effect/noCatchAllToMapError": "error",
|
|
29
|
+
"effect/noUnnecessaryPipeChain": "error",
|
|
30
|
+
"effect/noMultipleEffectProvide": "error",
|
|
31
|
+
"effect/noSchemaUnionOfLiterals": "warn",
|
|
32
|
+
"effect/noSchemaStructWithTag": "warn",
|
|
33
|
+
"effect/noRedundantSchemaTagIdentifier": "warn",
|
|
34
|
+
"effect/noEffectMapFlatten": "warn",
|
|
35
|
+
"effect/noPositionalLogError": "error",
|
|
29
36
|
|
|
30
|
-
// Effect-
|
|
31
|
-
"effect/
|
|
32
|
-
"effect/
|
|
33
|
-
"effect/
|
|
34
|
-
"effect/
|
|
35
|
-
"effect/
|
|
37
|
+
// Effect-enforcing bans (prevent escaping Effect model)
|
|
38
|
+
"effect/noThrowStatement": "error",
|
|
39
|
+
"effect/noTryCatch": "error",
|
|
40
|
+
"effect/noNewPromise": "error",
|
|
41
|
+
"effect/noNewError": "error",
|
|
42
|
+
"effect/noReturnNullish": "warn",
|
|
43
|
+
|
|
44
|
+
// Effect-context rules (inside Effect.gen/fn)
|
|
45
|
+
"effect/noGlobals": "error",
|
|
46
|
+
"effect/noInlineProvide": "error",
|
|
36
47
|
} as const
|
|
@@ -1,37 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Effect-native preset — for codebases fully committed to Effect.
|
|
3
|
-
*
|
|
3
|
+
* All Effect-context rules at error. Enforces Effect platform services
|
|
4
|
+
* and tagged errors inside generators.
|
|
4
5
|
*/
|
|
5
6
|
export const effectNative = {
|
|
6
|
-
// Global bans (everywhere)
|
|
7
|
-
"effect/
|
|
8
|
-
"effect/noGlobalConsole": "error",
|
|
9
|
-
"effect/noGlobalDate": "error",
|
|
10
|
-
"effect/noGlobalRandom": "error",
|
|
11
|
-
"effect/noGlobalTimers": "error",
|
|
12
|
-
"effect/noNewPromise": "error",
|
|
13
|
-
"effect/noNewError": "error",
|
|
14
|
-
"effect/noProcessEnv": "error",
|
|
7
|
+
// Global bans (everywhere, not just Effect context)
|
|
8
|
+
"effect/noAsyncFunction": "error",
|
|
15
9
|
"effect/noNodeBuiltinImport": "error",
|
|
10
|
+
|
|
11
|
+
// Effect-enforcing bans
|
|
16
12
|
"effect/noThrowStatement": "error",
|
|
17
13
|
"effect/noTryCatch": "error",
|
|
18
|
-
"effect/
|
|
19
|
-
"effect/
|
|
14
|
+
"effect/noNewPromise": "error",
|
|
15
|
+
"effect/noNewError": "error",
|
|
16
|
+
"effect/noReturnNullish": "error",
|
|
17
|
+
|
|
18
|
+
// Effect-context
|
|
19
|
+
"effect/noGlobals": "error",
|
|
20
|
+
"effect/noInlineProvide": "error",
|
|
21
|
+
|
|
22
|
+
// Error hygiene
|
|
23
|
+
"effect/noGlobalErrorInFailure": "error",
|
|
24
|
+
"effect/noGlobalErrorInCatch": "error",
|
|
20
25
|
"effect/noExtendsNativeError": "error",
|
|
21
|
-
"effect/noAsyncFunction": "error",
|
|
22
26
|
"effect/noInstanceofSchema": "error",
|
|
23
|
-
"effect/
|
|
24
|
-
"effect/noDynamicImport": "error",
|
|
25
|
-
"effect/noSwitchStatement": "error",
|
|
26
|
-
|
|
27
|
-
// Effect-context (inside Effect.gen/fn)
|
|
28
|
-
"effect/noFetchInEffect": "error",
|
|
29
|
-
"effect/noDateInEffect": "error",
|
|
30
|
-
"effect/noRandomInEffect": "error",
|
|
31
|
-
"effect/noTimersInEffect": "error",
|
|
32
|
-
"effect/noJsonInEffect": "error",
|
|
33
|
-
"effect/noProcessEnvInEffect": "error",
|
|
34
|
-
"effect/noPlatformGlobals": "error",
|
|
27
|
+
"effect/noPositionalLogError": "error",
|
|
35
28
|
|
|
36
29
|
// Type hygiene
|
|
37
30
|
"effect/noEffectTypeAlias": "error",
|
package/src/presets/full.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Full preset — all rules enabled.
|
|
2
|
+
* Full preset — all Effect-specific rules enabled.
|
|
3
|
+
*
|
|
4
|
+
* Does NOT include generic JS rules (no-console, no-throw, etc.) —
|
|
5
|
+
* configure those via oxlint's built-in rules in .oxlintrc.json.
|
|
3
6
|
*/
|
|
4
7
|
export const full = {
|
|
5
8
|
// API bans
|
|
@@ -12,42 +15,20 @@ export const full = {
|
|
|
12
15
|
"effect/noRuntimeRunFork": "error",
|
|
13
16
|
"effect/noRunInEffect": "error",
|
|
14
17
|
|
|
15
|
-
// Global bans
|
|
16
|
-
"effect/
|
|
17
|
-
"effect/noGlobalConsole": "warn",
|
|
18
|
-
"effect/noGlobalDate": "warn",
|
|
19
|
-
"effect/noGlobalRandom": "warn",
|
|
20
|
-
"effect/noGlobalTimers": "warn",
|
|
21
|
-
"effect/noNewPromise": "warn",
|
|
22
|
-
"effect/noNewError": "warn",
|
|
23
|
-
"effect/noProcessEnv": "warn",
|
|
24
|
-
|
|
25
|
-
// Import bans
|
|
18
|
+
// Global bans (everywhere, not just Effect context)
|
|
19
|
+
"effect/noAsyncFunction": "warn",
|
|
26
20
|
"effect/noNodeBuiltinImport": "warn",
|
|
27
|
-
"effect/noDynamicImport": "error",
|
|
28
|
-
|
|
29
|
-
// Statement bans
|
|
30
|
-
"effect/noThrowStatement": "error",
|
|
31
|
-
"effect/noTryCatch": "warn",
|
|
32
|
-
"effect/noReturnNull": "warn",
|
|
33
|
-
"effect/noSwitchStatement": "warn",
|
|
34
|
-
"effect/noIfStatement": "off",
|
|
35
|
-
"effect/noTernary": "off",
|
|
36
|
-
|
|
37
|
-
// JSON
|
|
38
|
-
"effect/noJsonParse": "warn",
|
|
39
21
|
|
|
40
22
|
// AST pattern rules
|
|
41
23
|
"effect/noNestedPipe": "error",
|
|
42
24
|
"effect/noNestedEffectGen": "error",
|
|
43
|
-
"effect/noUnnecessaryEffectGen": "
|
|
44
|
-
"effect/noEffectSucceedVoid": "
|
|
45
|
-
"effect/noEffectMapVoid": "
|
|
25
|
+
"effect/noUnnecessaryEffectGen": "error",
|
|
26
|
+
"effect/noEffectSucceedVoid": "error",
|
|
27
|
+
"effect/noEffectMapVoid": "error",
|
|
46
28
|
"effect/noUnnecessaryPipe": "warn",
|
|
47
|
-
"effect/noExtendsNativeError": "
|
|
29
|
+
"effect/noExtendsNativeError": "error",
|
|
48
30
|
"effect/noEffectFnGenerator": "warn",
|
|
49
|
-
"effect/noInstanceofSchema": "
|
|
50
|
-
"effect/noAsyncFunction": "warn",
|
|
31
|
+
"effect/noInstanceofSchema": "error",
|
|
51
32
|
"effect/noFlatmapLadder": "error",
|
|
52
33
|
"effect/noNestedEffectCall": "error",
|
|
53
34
|
"effect/noIifeWrapper": "warn",
|
|
@@ -55,7 +36,7 @@ export const full = {
|
|
|
55
36
|
"effect/noEffectOrElseLadder": "warn",
|
|
56
37
|
"effect/noFromNullableCoalesce": "warn",
|
|
57
38
|
"effect/noUnnecessaryArrowBlock": "warn",
|
|
58
|
-
"effect/noEffectGenAdapter": "
|
|
39
|
+
"effect/noEffectGenAdapter": "error",
|
|
59
40
|
"effect/noEffectSyncConsole": "error",
|
|
60
41
|
"effect/noEffectTypeAlias": "warn",
|
|
61
42
|
"effect/noEffectWrapperAlias": "warn",
|
|
@@ -63,22 +44,27 @@ export const full = {
|
|
|
63
44
|
"effect/noManualEffectChannels": "warn",
|
|
64
45
|
"effect/noMatchVoidBranch": "warn",
|
|
65
46
|
"effect/noOptionBooleanNormalization": "warn",
|
|
66
|
-
"effect/noReturnInArrow": "off",
|
|
67
|
-
"effect/noStringSentinelConst": "off",
|
|
68
47
|
"effect/noEffectSyncWrapper": "off",
|
|
69
|
-
"effect/noArrowLadder": "warn",
|
|
70
48
|
"effect/noMatchEffectBranch": "warn",
|
|
49
|
+
"effect/noCatchAllToMapError": "error",
|
|
50
|
+
"effect/noUnnecessaryPipeChain": "error",
|
|
51
|
+
"effect/noMultipleEffectProvide": "error",
|
|
52
|
+
"effect/noSchemaUnionOfLiterals": "warn",
|
|
53
|
+
"effect/noSchemaStructWithTag": "warn",
|
|
54
|
+
"effect/noRedundantSchemaTagIdentifier": "warn",
|
|
55
|
+
"effect/noEffectMapFlatten": "warn",
|
|
56
|
+
"effect/noGlobalErrorInFailure": "error",
|
|
57
|
+
"effect/noGlobalErrorInCatch": "error",
|
|
58
|
+
"effect/noPositionalLogError": "error",
|
|
59
|
+
|
|
60
|
+
// Effect-enforcing bans (prevent escaping Effect model)
|
|
61
|
+
"effect/noThrowStatement": "error",
|
|
62
|
+
"effect/noTryCatch": "error",
|
|
63
|
+
"effect/noNewPromise": "error",
|
|
64
|
+
"effect/noNewError": "error",
|
|
65
|
+
"effect/noReturnNullish": "warn",
|
|
71
66
|
|
|
72
|
-
// Effect-context rules
|
|
73
|
-
"effect/
|
|
74
|
-
"effect/
|
|
75
|
-
"effect/noConsoleInEffect": "warn",
|
|
76
|
-
"effect/noFetchInEffect": "warn",
|
|
77
|
-
"effect/noDateInEffect": "warn",
|
|
78
|
-
"effect/noRandomInEffect": "warn",
|
|
79
|
-
"effect/noTimersInEffect": "warn",
|
|
80
|
-
"effect/noJsonInEffect": "warn",
|
|
81
|
-
"effect/noProcessEnvInEffect": "warn",
|
|
82
|
-
"effect/noPlatformGlobals": "warn",
|
|
83
|
-
"effect/noInlineRuntimeProvide": "warn",
|
|
67
|
+
// Effect-context rules (inside Effect.gen/fn)
|
|
68
|
+
"effect/noGlobals": "error",
|
|
69
|
+
"effect/noInlineProvide": "error",
|
|
84
70
|
} as const
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functional preset — strict functional style for Effect code.
|
|
3
|
+
* Bans imperative patterns. Layer on top of core or full.
|
|
4
|
+
*/
|
|
5
|
+
export const functional = {
|
|
6
|
+
"effect/noIfStatement": "error",
|
|
7
|
+
"effect/noSwitchStatement": "error",
|
|
8
|
+
"effect/noTernary": "error",
|
|
9
|
+
"effect/noReturnInArrow": "warn",
|
|
10
|
+
"effect/noReturnNull": "warn",
|
|
11
|
+
"effect/noEffectSucceedVariable": "error",
|
|
12
|
+
"effect/noEffectSucceedString": "error",
|
|
13
|
+
"effect/noStringSentinelConst": "warn",
|
|
14
|
+
"effect/noMatchVoidBranch": "error",
|
|
15
|
+
"effect/noMatchEffectBranch": "error",
|
|
16
|
+
"effect/noOptionBooleanNormalization": "error",
|
|
17
|
+
"effect/noArrowLadder": "error",
|
|
18
|
+
} as const
|
package/src/presets/index.ts
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict preset — every rule at error. No warnings, no off.
|
|
3
|
+
* The nuclear option. Use when you want zero tolerance.
|
|
4
|
+
*/
|
|
5
|
+
export const strict = {
|
|
6
|
+
// API bans
|
|
7
|
+
"effect/noEffectDo": "error",
|
|
8
|
+
"effect/noEffectNever": "error",
|
|
9
|
+
"effect/noEffectAs": "error",
|
|
10
|
+
"effect/noEffectAsync": "error",
|
|
11
|
+
"effect/noEffectBind": "error",
|
|
12
|
+
"effect/noOptionAs": "error",
|
|
13
|
+
"effect/noRuntimeRunFork": "error",
|
|
14
|
+
"effect/noRunInEffect": "error",
|
|
15
|
+
|
|
16
|
+
// Style / functional
|
|
17
|
+
"effect/noReturnNull": "error",
|
|
18
|
+
"effect/noSwitchStatement": "error",
|
|
19
|
+
"effect/noIfStatement": "error",
|
|
20
|
+
"effect/noTernary": "error",
|
|
21
|
+
"effect/noReturnInArrow": "error",
|
|
22
|
+
"effect/noStringSentinelConst": "error",
|
|
23
|
+
"effect/noArrowLadder": "error",
|
|
24
|
+
|
|
25
|
+
// Global bans (everywhere, not just Effect context)
|
|
26
|
+
"effect/noAsyncFunction": "error",
|
|
27
|
+
"effect/noNodeBuiltinImport": "error",
|
|
28
|
+
|
|
29
|
+
// AST pattern rules
|
|
30
|
+
"effect/noNestedPipe": "error",
|
|
31
|
+
"effect/noNestedEffectGen": "error",
|
|
32
|
+
"effect/noUnnecessaryEffectGen": "error",
|
|
33
|
+
"effect/noEffectSucceedVoid": "error",
|
|
34
|
+
"effect/noEffectMapVoid": "error",
|
|
35
|
+
"effect/noUnnecessaryPipe": "error",
|
|
36
|
+
"effect/noExtendsNativeError": "error",
|
|
37
|
+
"effect/noEffectFnGenerator": "error",
|
|
38
|
+
"effect/noInstanceofSchema": "error",
|
|
39
|
+
"effect/noFlatmapLadder": "error",
|
|
40
|
+
"effect/noNestedEffectCall": "error",
|
|
41
|
+
"effect/noIifeWrapper": "error",
|
|
42
|
+
"effect/noEffectSucceedString": "error",
|
|
43
|
+
"effect/noEffectOrElseLadder": "error",
|
|
44
|
+
"effect/noFromNullableCoalesce": "error",
|
|
45
|
+
"effect/noUnnecessaryArrowBlock": "error",
|
|
46
|
+
"effect/noEffectGenAdapter": "error",
|
|
47
|
+
"effect/noEffectSyncConsole": "error",
|
|
48
|
+
"effect/noEffectTypeAlias": "error",
|
|
49
|
+
"effect/noEffectWrapperAlias": "error",
|
|
50
|
+
"effect/noEffectSucceedVariable": "error",
|
|
51
|
+
"effect/noManualEffectChannels": "error",
|
|
52
|
+
"effect/noMatchVoidBranch": "error",
|
|
53
|
+
"effect/noOptionBooleanNormalization": "error",
|
|
54
|
+
"effect/noEffectSyncWrapper": "error",
|
|
55
|
+
"effect/noMatchEffectBranch": "error",
|
|
56
|
+
"effect/noCatchAllToMapError": "error",
|
|
57
|
+
"effect/noUnnecessaryPipeChain": "error",
|
|
58
|
+
"effect/noMultipleEffectProvide": "error",
|
|
59
|
+
"effect/noSchemaUnionOfLiterals": "error",
|
|
60
|
+
"effect/noSchemaStructWithTag": "error",
|
|
61
|
+
"effect/noRedundantSchemaTagIdentifier": "error",
|
|
62
|
+
"effect/noEffectMapFlatten": "error",
|
|
63
|
+
"effect/noGlobalErrorInFailure": "error",
|
|
64
|
+
"effect/noGlobalErrorInCatch": "error",
|
|
65
|
+
"effect/noPositionalLogError": "error",
|
|
66
|
+
|
|
67
|
+
// Effect-enforcing bans
|
|
68
|
+
"effect/noThrowStatement": "error",
|
|
69
|
+
"effect/noTryCatch": "error",
|
|
70
|
+
"effect/noNewPromise": "error",
|
|
71
|
+
"effect/noNewError": "error",
|
|
72
|
+
"effect/noReturnNullish": "error",
|
|
73
|
+
|
|
74
|
+
// Effect-context rules
|
|
75
|
+
"effect/noGlobals": "error",
|
|
76
|
+
"effect/noInlineProvide": "error",
|
|
77
|
+
} as const
|
package/src/rules/index.ts
CHANGED
|
@@ -14,29 +14,17 @@ export { noOptionAs } from "./no-option-as.js"
|
|
|
14
14
|
export { noRunInEffect } from "./no-run-in-effect.js"
|
|
15
15
|
export { noRuntimeRunFork } from "./no-runtime-run-fork.js"
|
|
16
16
|
|
|
17
|
-
// --- Global bans ---
|
|
18
|
-
export { noGlobalConsole } from "./no-global-console.js"
|
|
19
|
-
export { noGlobalDate } from "./no-global-date.js"
|
|
20
|
-
export { noGlobalFetch } from "./no-global-fetch.js"
|
|
21
|
-
export { noGlobalRandom } from "./no-global-random.js"
|
|
22
|
-
export { noGlobalTimers } from "./no-global-timers.js"
|
|
23
|
-
export { noProcessEnv } from "./no-process-env.js"
|
|
24
|
-
|
|
25
17
|
// --- Statement bans ---
|
|
26
|
-
export { noDynamicImport } from "./no-dynamic-import.js"
|
|
27
18
|
export { noIfStatement } from "./no-if-statement.js"
|
|
28
19
|
export { noSwitchStatement } from "./no-switch-statement.js"
|
|
29
|
-
export { noThrowStatement } from "./no-throw-statement.js"
|
|
30
|
-
export { noTryCatch } from "./no-try-catch.js"
|
|
31
|
-
|
|
32
|
-
// --- JSON ---
|
|
33
|
-
export { noJsonParse } from "./no-json-parse.js"
|
|
34
20
|
|
|
35
21
|
// --- AST pattern rules ---
|
|
36
22
|
export { noArrowLadder } from "./no-arrow-ladder.js"
|
|
37
23
|
export { noAsyncFunction } from "./no-async-function.js"
|
|
24
|
+
export { noCatchAllToMapError } from "./no-catch-all-to-map-error.js"
|
|
38
25
|
export { noEffectFnGenerator } from "./no-effect-fn-generator.js"
|
|
39
26
|
export { noEffectGenAdapter } from "./no-effect-gen-adapter.js"
|
|
27
|
+
export { noEffectMapFlatten } from "./no-effect-map-flatten.js"
|
|
40
28
|
export { noEffectMapVoid } from "./no-effect-map-void.js"
|
|
41
29
|
export { noEffectOrElseLadder } from "./no-effect-orElse-ladder.js"
|
|
42
30
|
export { noEffectSucceedString } from "./no-effect-succeed-string.js"
|
|
@@ -49,35 +37,36 @@ export { noEffectWrapperAlias } from "./no-effect-wrapper-alias.js"
|
|
|
49
37
|
export { noExtendsNativeError } from "./no-extends-native-error.js"
|
|
50
38
|
export { noFlatmapLadder } from "./no-flatmap-ladder.js"
|
|
51
39
|
export { noFromNullableCoalesce } from "./no-fromnullable-coalesce.js"
|
|
40
|
+
export { noGlobalErrorInCatch } from "./no-global-error-in-catch.js"
|
|
41
|
+
export { noGlobalErrorInFailure } from "./no-global-error-in-failure.js"
|
|
52
42
|
export { noIifeWrapper } from "./no-iife-wrapper.js"
|
|
53
43
|
export { noInstanceofSchema } from "./no-instanceof-schema.js"
|
|
54
44
|
export { noManualEffectChannels } from "./no-manual-effect-channels.js"
|
|
55
45
|
export { noMatchEffectBranch } from "./no-match-effect-branch.js"
|
|
56
46
|
export { noMatchVoidBranch } from "./no-match-void-branch.js"
|
|
47
|
+
export { noMultipleEffectProvide } from "./no-multiple-effect-provide.js"
|
|
57
48
|
export { noNestedEffectCall } from "./no-nested-effect-call.js"
|
|
58
49
|
export { noNestedEffectGen } from "./no-nested-effect-gen.js"
|
|
59
50
|
export { noNestedPipe } from "./no-nested-pipe.js"
|
|
60
|
-
export { noNewError } from "./no-new-error.js"
|
|
61
|
-
export { noNewPromise } from "./no-new-promise.js"
|
|
62
51
|
export { noNodeBuiltinImport } from "./no-node-builtin-import.js"
|
|
63
52
|
export { noOptionBooleanNormalization } from "./no-option-boolean-normalization.js"
|
|
53
|
+
export { noPositionalLogError } from "./no-positional-log-error.js"
|
|
54
|
+
export { noRedundantSchemaTagIdentifier } from "./no-redundant-schema-tag-identifier.js"
|
|
64
55
|
export { noReturnInArrow } from "./no-return-in-arrow.js"
|
|
65
|
-
export {
|
|
56
|
+
export { noSchemaStructWithTag } from "./no-schema-struct-with-tag.js"
|
|
57
|
+
export { noSchemaUnionOfLiterals } from "./no-schema-union-of-literals.js"
|
|
66
58
|
export { noStringSentinelConst } from "./no-string-sentinel-const.js"
|
|
67
59
|
export { noTernary } from "./no-ternary.js"
|
|
68
60
|
export { noUnnecessaryArrowBlock } from "./no-unnecessary-arrow-block.js"
|
|
69
61
|
export { noUnnecessaryEffectGen } from "./no-unnecessary-effect-gen.js"
|
|
62
|
+
export { noUnnecessaryPipeChain } from "./no-unnecessary-pipe-chain.js"
|
|
70
63
|
export { noUnnecessaryPipe } from "./no-unnecessary-pipe.js"
|
|
71
64
|
|
|
72
65
|
// --- Effect-context rules ---
|
|
73
|
-
export {
|
|
74
|
-
export {
|
|
75
|
-
export {
|
|
76
|
-
export {
|
|
77
|
-
export {
|
|
78
|
-
export {
|
|
79
|
-
export {
|
|
80
|
-
export { noRandomInEffect } from "./no-random-in-effect.js"
|
|
81
|
-
export { noThrowInEffectGen } from "./no-throw-in-effect-gen.js"
|
|
82
|
-
export { noTimersInEffect } from "./no-timers-in-effect.js"
|
|
83
|
-
export { noTryCatchInEffectGen } from "./no-try-catch-in-effect-gen.js"
|
|
66
|
+
export { noGlobals } from "./no-globals.js"
|
|
67
|
+
export { noInlineProvide } from "./no-inline-provide.js"
|
|
68
|
+
export { noNewError } from "./no-new-error.js"
|
|
69
|
+
export { noNewPromise } from "./no-new-promise.js"
|
|
70
|
+
export { noReturnNullish } from "./no-return-nullish.js"
|
|
71
|
+
export { noThrowStatement } from "./no-throw-statement.js"
|
|
72
|
+
export { noTryCatch } from "./no-try-catch.js"
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Source: biome-effect-linting-rules/no-arrow-ladder
|
|
7
7
|
*/
|
|
8
|
-
import { Diagnostic, Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
8
|
+
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
9
|
import * as Effect from "effect/Effect"
|
|
11
10
|
|
|
12
11
|
const isIife = (node: unknown): boolean => {
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
* Note: This is an effectNative rule — off by default.
|
|
9
9
|
* Only enable in codebases fully committed to Effect.
|
|
10
10
|
*/
|
|
11
|
-
import { Diagnostic, Rule } from "../vendor/effect-oxlint/index.js"
|
|
12
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
+
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
13
12
|
import * as Effect from "effect/Effect"
|
|
14
13
|
|
|
15
14
|
export const noAsyncFunction = Rule.define({
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ban `Effect.catchAll(e => Effect.fail(f(e)))` — use `Effect.mapError(f)`.
|
|
3
|
+
*
|
|
4
|
+
* Source: language-service/catchAllToMapError
|
|
5
|
+
*/
|
|
6
|
+
import type { ESTree } from "@oxlint/plugins"
|
|
7
|
+
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as Option from "effect/Option"
|
|
10
|
+
|
|
11
|
+
const catchNames = new Set(["catchAll", "catchAllCause"])
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if a node is `Effect.fail(...)` or `Effect.failCause(...)`.
|
|
15
|
+
*/
|
|
16
|
+
const isEffectFailCall = (node: ESTree.Node): boolean => {
|
|
17
|
+
if (node.type !== "CallExpression") return false
|
|
18
|
+
const call = node as ESTree.CallExpression
|
|
19
|
+
return (
|
|
20
|
+
Option.isSome(AST.matchCallOf(call, "Effect", "fail")) ||
|
|
21
|
+
Option.isSome(AST.matchCallOf(call, "Effect", "failCause"))
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a function body is a single `Effect.fail(...)` expression.
|
|
27
|
+
* Handles: `(e) => Effect.fail(f(e))` and `(e) => { return Effect.fail(f(e)) }`
|
|
28
|
+
*/
|
|
29
|
+
const bodyIsEffectFail = (fn: ESTree.Node): boolean => {
|
|
30
|
+
const body = (fn as unknown as Record<string, unknown>)["body"]
|
|
31
|
+
if (body == null || typeof body !== "object") return false
|
|
32
|
+
const bodyNode = body as ESTree.Node
|
|
33
|
+
// Arrow with expression body: (e) => Effect.fail(f(e))
|
|
34
|
+
if (bodyNode.type !== "BlockStatement") {
|
|
35
|
+
return isEffectFailCall(bodyNode)
|
|
36
|
+
}
|
|
37
|
+
// Block body with single return: (e) => { return Effect.fail(f(e)) }
|
|
38
|
+
const stmts = (bodyNode as unknown as Record<string, unknown>)["body"]
|
|
39
|
+
if (!Array.isArray(stmts) || stmts.length !== 1) return false
|
|
40
|
+
const stmt = stmts[0] as ESTree.Node
|
|
41
|
+
if (stmt.type !== "ReturnStatement") return false
|
|
42
|
+
const arg = (stmt as unknown as Record<string, unknown>)["argument"]
|
|
43
|
+
return arg != null && typeof arg === "object" && isEffectFailCall(arg as ESTree.Node)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const noCatchAllToMapError = Rule.define({
|
|
47
|
+
name: "no-catch-all-to-map-error",
|
|
48
|
+
meta: Rule.meta({
|
|
49
|
+
type: "suggestion",
|
|
50
|
+
description:
|
|
51
|
+
"Use Effect.mapError instead of Effect.catchAll(e => Effect.fail(f(e))).",
|
|
52
|
+
}),
|
|
53
|
+
create: function* () {
|
|
54
|
+
const ctx = yield* RuleContext
|
|
55
|
+
return {
|
|
56
|
+
CallExpression: (node) => {
|
|
57
|
+
const call = node as ESTree.CallExpression
|
|
58
|
+
const names = Option.getOrUndefined(AST.memberNames(call.callee as ESTree.MemberExpression))
|
|
59
|
+
if (names === undefined) return Effect.void
|
|
60
|
+
const [obj, prop] = names
|
|
61
|
+
if (obj !== "Effect" && obj !== "_") return Effect.void
|
|
62
|
+
if (!catchNames.has(prop)) return Effect.void
|
|
63
|
+
|
|
64
|
+
// The callback is the last argument
|
|
65
|
+
const args = call.arguments
|
|
66
|
+
if (args.length === 0) return Effect.void
|
|
67
|
+
const callback = args[args.length - 1]
|
|
68
|
+
if (callback === undefined) return Effect.void
|
|
69
|
+
if (
|
|
70
|
+
callback.type !== "ArrowFunctionExpression" &&
|
|
71
|
+
callback.type !== "FunctionExpression"
|
|
72
|
+
) {
|
|
73
|
+
return Effect.void
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (bodyIsEffectFail(callback as ESTree.Node)) {
|
|
77
|
+
const replacement = prop === "catchAllCause" ? "mapErrorCause" : "mapError"
|
|
78
|
+
return ctx.report(
|
|
79
|
+
Diagnostic.make({
|
|
80
|
+
node,
|
|
81
|
+
message: `Use Effect.${replacement} instead of Effect.${prop}(e => Effect.fail(...)).`,
|
|
82
|
+
}),
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
return Effect.void
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
})
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
* a name span, which is the discouraged pattern from the biome rules.
|
|
12
12
|
*/
|
|
13
13
|
import type { ESTree } from "@oxlint/plugins"
|
|
14
|
-
import { AST, Diagnostic, Rule } from "../vendor/effect-oxlint/index.js"
|
|
15
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
14
|
+
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
16
15
|
import * as Effect from "effect/Effect"
|
|
17
16
|
|
|
18
17
|
export const noEffectFnGenerator = Rule.define({
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* Source: language-service/effectGenUsesAdapter
|
|
7
7
|
*/
|
|
8
8
|
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
9
|
+
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
10
|
import * as Effect from "effect/Effect"
|
|
12
11
|
|
|
13
12
|
export const noEffectGenAdapter = Rule.define({
|