oxlint-plugin-effect 0.0.0 → 0.2.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/package.json +6 -7
  3. package/src/presets/core.ts +24 -13
  4. package/src/presets/effect-native.ts +18 -25
  5. package/src/presets/full.ts +33 -47
  6. package/src/presets/functional.ts +18 -0
  7. package/src/presets/index.ts +2 -0
  8. package/src/presets/strict.ts +77 -0
  9. package/src/rules/index.ts +18 -29
  10. package/src/rules/no-arrow-ladder.ts +1 -2
  11. package/src/rules/no-async-function.ts +1 -2
  12. package/src/rules/no-catch-all-to-map-error.ts +90 -0
  13. package/src/rules/no-effect-fn-generator.ts +1 -2
  14. package/src/rules/no-effect-gen-adapter.ts +2 -2
  15. package/src/rules/no-effect-map-flatten.ts +82 -0
  16. package/src/rules/no-effect-map-void.ts +1 -2
  17. package/src/rules/no-effect-orElse-ladder.ts +1 -2
  18. package/src/rules/no-effect-succeed-string.ts +1 -2
  19. package/src/rules/no-effect-succeed-variable.ts +1 -2
  20. package/src/rules/no-effect-succeed-void.ts +1 -2
  21. package/src/rules/no-effect-sync-console.ts +1 -2
  22. package/src/rules/no-effect-sync-wrapper.ts +1 -2
  23. package/src/rules/no-effect-type-alias.ts +1 -2
  24. package/src/rules/no-effect-wrapper-alias.ts +1 -4
  25. package/src/rules/no-extends-native-error.ts +1 -2
  26. package/src/rules/no-flatmap-ladder.ts +1 -3
  27. package/src/rules/no-fromnullable-coalesce.ts +1 -2
  28. package/src/rules/no-global-error-in-catch.ts +104 -0
  29. package/src/rules/no-global-error-in-failure.ts +83 -0
  30. package/src/rules/no-globals.ts +178 -0
  31. package/src/rules/no-iife-wrapper.ts +1 -2
  32. package/src/rules/{no-inline-runtime-provide.ts → no-inline-provide.ts} +3 -4
  33. package/src/rules/no-instanceof-schema.ts +2 -3
  34. package/src/rules/no-manual-effect-channels.ts +1 -2
  35. package/src/rules/no-match-effect-branch.ts +1 -2
  36. package/src/rules/no-match-void-branch.ts +1 -2
  37. package/src/rules/no-multiple-effect-provide.ts +75 -0
  38. package/src/rules/no-nested-effect-call.ts +1 -3
  39. package/src/rules/no-nested-effect-gen.ts +1 -2
  40. package/src/rules/no-nested-pipe.ts +1 -4
  41. package/src/rules/no-new-error.ts +46 -10
  42. package/src/rules/no-new-promise.ts +43 -7
  43. package/src/rules/no-node-builtin-import.ts +1 -3
  44. package/src/rules/no-option-boolean-normalization.ts +1 -2
  45. package/src/rules/no-positional-log-error.ts +76 -0
  46. package/src/rules/no-redundant-schema-tag-identifier.ts +62 -0
  47. package/src/rules/no-return-in-arrow.ts +1 -2
  48. package/src/rules/no-return-nullish.ts +80 -0
  49. package/src/rules/no-run-in-effect-gen.ts +66 -0
  50. package/src/rules/no-runtime-run-fork.ts +1 -1
  51. package/src/rules/no-schema-struct-with-tag.ts +64 -0
  52. package/src/rules/no-schema-union-of-literals.ts +48 -0
  53. package/src/rules/no-string-sentinel-const.ts +1 -2
  54. package/src/rules/no-ternary.ts +1 -3
  55. package/src/rules/no-throw-statement.ts +32 -6
  56. package/src/rules/no-try-catch.ts +32 -7
  57. package/src/rules/no-unnecessary-arrow-block.ts +1 -2
  58. package/src/rules/no-unnecessary-effect-gen.ts +1 -3
  59. package/src/rules/no-unnecessary-pipe-chain.ts +47 -0
  60. package/src/rules/no-unnecessary-pipe.ts +1 -2
  61. package/src/vendor/effect-oxlint/Rule.ts +141 -183
  62. package/tests/ban-rules.test.ts +0 -184
  63. package/tests/context-rules.test.ts +127 -145
  64. package/tests/pattern-rules.test.ts +39 -50
  65. package/src/rules/no-console-in-effect.ts +0 -46
  66. package/src/rules/no-date-in-effect.ts +0 -61
  67. package/src/rules/no-dynamic-import.ts +0 -13
  68. package/src/rules/no-fetch-in-effect.ts +0 -47
  69. package/src/rules/no-global-console.ts +0 -17
  70. package/src/rules/no-global-date.ts +0 -20
  71. package/src/rules/no-global-fetch.ts +0 -14
  72. package/src/rules/no-global-random.ts +0 -13
  73. package/src/rules/no-global-timers.ts +0 -17
  74. package/src/rules/no-json-in-effect.ts +0 -45
  75. package/src/rules/no-json-parse.ts +0 -18
  76. package/src/rules/no-platform-globals.ts +0 -117
  77. package/src/rules/no-process-env-in-effect.ts +0 -39
  78. package/src/rules/no-process-env.ts +0 -13
  79. package/src/rules/no-random-in-effect.ts +0 -39
  80. package/src/rules/no-return-null.ts +0 -47
  81. package/src/rules/no-run-in-effect.ts +0 -18
  82. package/src/rules/no-throw-in-effect-gen.ts +0 -42
  83. package/src/rules/no-timers-in-effect.ts +0 -47
  84. package/src/rules/no-try-catch-in-effect-gen.ts +0 -42
package/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
1
+ # oxlint-plugin-effect
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`5ecb6fd`](https://github.com/cevr/effect-oxlint/commit/5ecb6fd543d6183d34f037c407f0a9cc0defc5e6) Thanks [@cevr](https://github.com/cevr)! - Support v3/v4 dual-version codebases and add rule-version metadata.
8
+
9
+ **`tsgolint-effect` (Go type-aware linter):**
10
+
11
+ - `IsEffectPackageSymbol` now matches paths across all mainstream package managers (npm/yarn-classic `node_modules/effect`, pnpm `.pnpm/effect@`, bun isolated `.bun/effect@`, yarn berry `.yarn/cache/effect-npm-`) and the `effect-v3` alias used by dual-version projects. Previously, v3 code imported as `effect-v3` silently bypassed every type-aware rule.
12
+ - `IsEffectType`, `IsLayerType`, `IsStreamType`, `IsScopeType` now check the `~effect/*` brand property on types first, falling back to the symbol-name + path heuristic. More robust across package managers and future-proof against package-name changes.
13
+ - Added `Rule.EffectVersion` field (`"v3" | "v4" | "both"`, default `"both"`) for version-specific rules.
14
+
15
+ **`oxlint-plugin-effect` (JS AST linter):**
16
+
17
+ - Added `meta.docs.effectVersion` on rules (`"v3" | "v4" | "both"`, default `"both"`). Preset authors can filter rules per project version.
18
+ - Tagged the following rules as `"v3"` (they detect v3-only APIs): `noCatchAllToMapError`, `noEffectGenAdapter`, `noRuntimeRunFork`.
19
+ - **Renamed `noRunInEffect` → `noRunInEffectGen`.** The old rule fired globally (outside Effect context too — a bug). The new rule is context-aware via `makeEffectContextTracker` and only fires inside `Effect.gen`/`Effect.fn`. Expanded coverage to `Runtime.run{Sync,Promise,Fork,Callback}`. Explicitly permits `Effect.run*With` (v4 explicit-services variants) and `<identifier>.run*` (ManagedRuntime-shaped calls) — both are legitimate at callback boundaries. Preset users must update `"effect/noRunInEffect"` references to `"effect/noRunInEffectGen"`.
20
+
21
+ ## 0.1.0
22
+
23
+ ### Minor Changes
24
+
25
+ - [`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.
26
+
27
+ **JS plugin (59 rules, was 66):**
28
+
29
+ New rules:
30
+
31
+ - noCatchAllToMapError, noUnnecessaryPipeChain, noMultipleEffectProvide
32
+ - noSchemaUnionOfLiterals, noSchemaStructWithTag, noRedundantSchemaTagIdentifier
33
+ - noEffectMapFlatten, noGlobalErrorInFailure, noGlobalErrorInCatch
34
+ - noPositionalLogError (from agent session analysis — 57 violations)
35
+ - noReturnNullish (catches null, undefined, void 0 → points to Option.none())
36
+ - noGlobals (consolidated context rule — console, fetch, Date, Math.random, crypto, timers, JSON, process/Bun/Deno with per-API Effect alternative hints)
37
+
38
+ 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.
39
+
40
+ Restored 5 Effect-enforcing rules that prevent escaping the Effect model:
41
+
42
+ - noThrowStatement → "Use yield* Effect.fail() or yield* new MyError()"
43
+ - noTryCatch → "Use Effect.try / Effect.tryPromise"
44
+ - noNewPromise → "Use Effect.async / Effect.tryPromise / Effect.promise"
45
+ - noNewError → "Define class MyError extends Schema.TaggedErrorClass..."
46
+ - noReturnNullish → "Use Option.none() / Effect.void"
47
+
48
+ Redesigned Rule.banMultiple API with per-spec BanSpec discriminated union.
49
+ Promoted high-frequency agent violations from warn to error in core/full presets.
50
+
51
+ **Go binary (24 rules, was 8):**
52
+
53
+ - Layer/leak detection: missingEffectContext, missingEffectError, missingLayerContext, leakingRequirements, layerMergeAllWithDependencies
54
+ - Error hygiene: anyUnknownInError, unknownInEffectCatch
55
+ - 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.0.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "oxlint plugin with Effect-specific lint rules",
6
6
  "license": "MIT",
7
- "repository": { "type": "git", "url": "https://github.com/cevr/effect-oxlint" },
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",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Core preset — essential Effect rules without global bans.
2
+ * Core preset — essential Effect rules.
3
3
  * Good starting point for projects transitioning to Effect.
4
4
  */
5
5
  export const core = {
@@ -8,14 +8,14 @@ export const core = {
8
8
  "effect/noEffectNever": "error",
9
9
  "effect/noEffectAs": "error",
10
10
  "effect/noEffectBind": "error",
11
- "effect/noRunInEffect": "error",
11
+ "effect/noRunInEffectGen": "error",
12
12
 
13
13
  // AST pattern rules
14
14
  "effect/noNestedPipe": "error",
15
15
  "effect/noNestedEffectGen": "error",
16
- "effect/noUnnecessaryEffectGen": "warn",
17
- "effect/noEffectSucceedVoid": "warn",
18
- "effect/noEffectMapVoid": "warn",
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": "warn",
26
+ "effect/noEffectGenAdapter": "error",
27
27
  "effect/noEffectSyncConsole": "error",
28
- "effect/noArrowLadder": "warn",
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-context rules
31
- "effect/noThrowInEffectGen": "error",
32
- "effect/noTryCatchInEffectGen": "warn",
33
- "effect/noConsoleInEffect": "warn",
34
- "effect/noInlineRuntimeProvide": "warn",
35
- "effect/noPlatformGlobals": "warn",
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
- * Bans all globals and builtins in favor of Effect platform services.
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/noGlobalFetch": "error",
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/noReturnNull": "error",
19
- "effect/noJsonParse": "error",
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/noRuntimeRunFork": "error",
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",
@@ -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
@@ -10,44 +13,22 @@ export const full = {
10
13
  "effect/noEffectBind": "error",
11
14
  "effect/noOptionAs": "error",
12
15
  "effect/noRuntimeRunFork": "error",
13
- "effect/noRunInEffect": "error",
16
+ "effect/noRunInEffectGen": "error",
14
17
 
15
- // Global bans
16
- "effect/noGlobalFetch": "warn",
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": "warn",
44
- "effect/noEffectSucceedVoid": "warn",
45
- "effect/noEffectMapVoid": "warn",
25
+ "effect/noUnnecessaryEffectGen": "error",
26
+ "effect/noEffectSucceedVoid": "error",
27
+ "effect/noEffectMapVoid": "error",
46
28
  "effect/noUnnecessaryPipe": "warn",
47
- "effect/noExtendsNativeError": "warn",
29
+ "effect/noExtendsNativeError": "error",
48
30
  "effect/noEffectFnGenerator": "warn",
49
- "effect/noInstanceofSchema": "warn",
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": "warn",
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/noThrowInEffectGen": "error",
74
- "effect/noTryCatchInEffectGen": "warn",
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
@@ -1,3 +1,5 @@
1
1
  export { full } from "./full.js"
2
2
  export { core } from "./core.js"
3
3
  export { effectNative } from "./effect-native.js"
4
+ export { functional } from "./functional.js"
5
+ export { strict } from "./strict.js"
@@ -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/noRunInEffectGen": "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
@@ -11,32 +11,19 @@ export { noEffectBind } from "./no-effect-bind.js"
11
11
  export { noEffectDo } from "./no-effect-do.js"
12
12
  export { noEffectNever } from "./no-effect-never.js"
13
13
  export { noOptionAs } from "./no-option-as.js"
14
- export { noRunInEffect } from "./no-run-in-effect.js"
15
14
  export { noRuntimeRunFork } from "./no-runtime-run-fork.js"
16
15
 
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
16
  // --- Statement bans ---
26
- export { noDynamicImport } from "./no-dynamic-import.js"
27
17
  export { noIfStatement } from "./no-if-statement.js"
28
18
  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
19
 
35
20
  // --- AST pattern rules ---
36
21
  export { noArrowLadder } from "./no-arrow-ladder.js"
37
22
  export { noAsyncFunction } from "./no-async-function.js"
23
+ export { noCatchAllToMapError } from "./no-catch-all-to-map-error.js"
38
24
  export { noEffectFnGenerator } from "./no-effect-fn-generator.js"
39
25
  export { noEffectGenAdapter } from "./no-effect-gen-adapter.js"
26
+ export { noEffectMapFlatten } from "./no-effect-map-flatten.js"
40
27
  export { noEffectMapVoid } from "./no-effect-map-void.js"
41
28
  export { noEffectOrElseLadder } from "./no-effect-orElse-ladder.js"
42
29
  export { noEffectSucceedString } from "./no-effect-succeed-string.js"
@@ -49,35 +36,37 @@ export { noEffectWrapperAlias } from "./no-effect-wrapper-alias.js"
49
36
  export { noExtendsNativeError } from "./no-extends-native-error.js"
50
37
  export { noFlatmapLadder } from "./no-flatmap-ladder.js"
51
38
  export { noFromNullableCoalesce } from "./no-fromnullable-coalesce.js"
39
+ export { noGlobalErrorInCatch } from "./no-global-error-in-catch.js"
40
+ export { noGlobalErrorInFailure } from "./no-global-error-in-failure.js"
52
41
  export { noIifeWrapper } from "./no-iife-wrapper.js"
53
42
  export { noInstanceofSchema } from "./no-instanceof-schema.js"
54
43
  export { noManualEffectChannels } from "./no-manual-effect-channels.js"
55
44
  export { noMatchEffectBranch } from "./no-match-effect-branch.js"
56
45
  export { noMatchVoidBranch } from "./no-match-void-branch.js"
46
+ export { noMultipleEffectProvide } from "./no-multiple-effect-provide.js"
57
47
  export { noNestedEffectCall } from "./no-nested-effect-call.js"
58
48
  export { noNestedEffectGen } from "./no-nested-effect-gen.js"
59
49
  export { noNestedPipe } from "./no-nested-pipe.js"
60
- export { noNewError } from "./no-new-error.js"
61
- export { noNewPromise } from "./no-new-promise.js"
62
50
  export { noNodeBuiltinImport } from "./no-node-builtin-import.js"
63
51
  export { noOptionBooleanNormalization } from "./no-option-boolean-normalization.js"
52
+ export { noPositionalLogError } from "./no-positional-log-error.js"
53
+ export { noRedundantSchemaTagIdentifier } from "./no-redundant-schema-tag-identifier.js"
64
54
  export { noReturnInArrow } from "./no-return-in-arrow.js"
65
- export { noReturnNull } from "./no-return-null.js"
55
+ export { noSchemaStructWithTag } from "./no-schema-struct-with-tag.js"
56
+ export { noSchemaUnionOfLiterals } from "./no-schema-union-of-literals.js"
66
57
  export { noStringSentinelConst } from "./no-string-sentinel-const.js"
67
58
  export { noTernary } from "./no-ternary.js"
68
59
  export { noUnnecessaryArrowBlock } from "./no-unnecessary-arrow-block.js"
69
60
  export { noUnnecessaryEffectGen } from "./no-unnecessary-effect-gen.js"
61
+ export { noUnnecessaryPipeChain } from "./no-unnecessary-pipe-chain.js"
70
62
  export { noUnnecessaryPipe } from "./no-unnecessary-pipe.js"
71
63
 
72
64
  // --- Effect-context rules ---
73
- export { noConsoleInEffect } from "./no-console-in-effect.js"
74
- export { noDateInEffect } from "./no-date-in-effect.js"
75
- export { noFetchInEffect } from "./no-fetch-in-effect.js"
76
- export { noInlineRuntimeProvide } from "./no-inline-runtime-provide.js"
77
- export { noJsonInEffect } from "./no-json-in-effect.js"
78
- export { noPlatformGlobals } from "./no-platform-globals.js"
79
- export { noProcessEnvInEffect } from "./no-process-env-in-effect.js"
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"
65
+ export { noGlobals } from "./no-globals.js"
66
+ export { noInlineProvide } from "./no-inline-provide.js"
67
+ export { noNewError } from "./no-new-error.js"
68
+ export { noNewPromise } from "./no-new-promise.js"
69
+ export { noReturnNullish } from "./no-return-nullish.js"
70
+ export { noRunInEffectGen } from "./no-run-in-effect-gen.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,90 @@
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
+ effectVersion: "v3",
53
+ }),
54
+ create: function* () {
55
+ const ctx = yield* RuleContext
56
+ return {
57
+ CallExpression: (node) => {
58
+ const call = node as ESTree.CallExpression
59
+ const names = Option.getOrUndefined(AST.memberNames(call.callee as ESTree.MemberExpression))
60
+ if (names === undefined) return Effect.void
61
+ const [obj, prop] = names
62
+ if (obj !== "Effect" && obj !== "_") return Effect.void
63
+ if (!catchNames.has(prop)) return Effect.void
64
+
65
+ // The callback is the last argument
66
+ const args = call.arguments
67
+ if (args.length === 0) return Effect.void
68
+ const callback = args[args.length - 1]
69
+ if (callback === undefined) return Effect.void
70
+ if (
71
+ callback.type !== "ArrowFunctionExpression" &&
72
+ callback.type !== "FunctionExpression"
73
+ ) {
74
+ return Effect.void
75
+ }
76
+
77
+ if (bodyIsEffectFail(callback as ESTree.Node)) {
78
+ const replacement = prop === "catchAllCause" ? "mapErrorCause" : "mapError"
79
+ return ctx.report(
80
+ Diagnostic.make({
81
+ node,
82
+ message: `Use Effect.${replacement} instead of Effect.${prop}(e => Effect.fail(...)).`,
83
+ }),
84
+ )
85
+ }
86
+ return Effect.void
87
+ },
88
+ }
89
+ },
90
+ })
@@ -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({