oxlint-plugin-effect 0.2.0 → 0.2.2
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/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.js +18 -0
- package/dist/presets/core.d.ts +43 -0
- package/dist/presets/core.js +43 -0
- package/dist/presets/effect-native.d.ts +28 -0
- package/dist/presets/effect-native.js +28 -0
- package/dist/presets/full.d.ts +60 -0
- package/dist/presets/full.js +60 -0
- package/dist/presets/functional.d.ts +21 -0
- package/dist/presets/functional.js +21 -0
- package/dist/presets/index.d.ts +14 -0
- package/dist/presets/index.js +20 -0
- package/dist/presets/strict.d.ts +64 -0
- package/dist/presets/strict.js +64 -0
- package/dist/presets/v3-strict.d.ts +16 -0
- package/dist/presets/v3-strict.js +16 -0
- package/dist/presets/v3.d.ts +18 -0
- package/dist/presets/v3.js +18 -0
- package/dist/rules/_effect-context.d.ts +19 -0
- package/dist/rules/_effect-context.js +22 -0
- package/dist/rules/index.d.ts +66 -0
- package/dist/rules/index.js +124 -0
- package/dist/rules/no-arrow-ladder.d.ts +6 -0
- package/dist/rules/no-arrow-ladder.js +61 -0
- package/dist/rules/no-async-function.d.ts +6 -0
- package/dist/rules/no-async-function.js +46 -0
- package/dist/rules/no-catch-all-to-map-error.d.ts +6 -0
- package/dist/rules/no-catch-all-to-map-error.js +67 -0
- package/dist/rules/no-effect-as.d.ts +6 -0
- package/dist/rules/no-effect-as.js +16 -0
- package/dist/rules/no-effect-async.d.ts +6 -0
- package/dist/rules/no-effect-async.js +16 -0
- package/dist/rules/no-effect-bind.d.ts +6 -0
- package/dist/rules/no-effect-bind.js +16 -0
- package/dist/rules/no-effect-do.d.ts +6 -0
- package/dist/rules/no-effect-do.js +16 -0
- package/dist/rules/no-effect-fn-generator.d.ts +6 -0
- package/dist/rules/no-effect-fn-generator.js +30 -0
- package/dist/rules/no-effect-gen-adapter.d.ts +6 -0
- package/dist/rules/no-effect-gen-adapter.js +30 -0
- package/dist/rules/no-effect-map-flatten.d.ts +6 -0
- package/dist/rules/no-effect-map-flatten.js +53 -0
- package/dist/rules/no-effect-map-void.d.ts +6 -0
- package/dist/rules/no-effect-map-void.js +40 -0
- package/dist/rules/no-effect-never.d.ts +6 -0
- package/dist/rules/no-effect-never.js +16 -0
- package/dist/rules/no-effect-orElse-ladder.d.ts +6 -0
- package/dist/rules/no-effect-orElse-ladder.js +53 -0
- package/dist/rules/no-effect-succeed-string.d.ts +6 -0
- package/dist/rules/no-effect-succeed-string.js +30 -0
- package/dist/rules/no-effect-succeed-variable.d.ts +6 -0
- package/dist/rules/no-effect-succeed-variable.js +30 -0
- package/dist/rules/no-effect-succeed-void.d.ts +6 -0
- package/dist/rules/no-effect-succeed-void.js +33 -0
- package/dist/rules/no-effect-sync-console.d.ts +6 -0
- package/dist/rules/no-effect-sync-console.js +63 -0
- package/dist/rules/no-effect-sync-wrapper.d.ts +6 -0
- package/dist/rules/no-effect-sync-wrapper.js +37 -0
- package/dist/rules/no-effect-type-alias.d.ts +6 -0
- package/dist/rules/no-effect-type-alias.js +52 -0
- package/dist/rules/no-effect-wrapper-alias.d.ts +6 -0
- package/dist/rules/no-effect-wrapper-alias.js +69 -0
- package/dist/rules/no-extends-native-error.d.ts +6 -0
- package/dist/rules/no-extends-native-error.js +41 -0
- package/dist/rules/no-flatmap-ladder.d.ts +6 -0
- package/dist/rules/no-flatmap-ladder.js +48 -0
- package/dist/rules/no-fromnullable-coalesce.d.ts +6 -0
- package/dist/rules/no-fromnullable-coalesce.js +36 -0
- package/dist/rules/no-global-error-in-catch.d.ts +6 -0
- package/dist/rules/no-global-error-in-catch.js +75 -0
- package/dist/rules/no-global-error-in-failure.d.ts +6 -0
- package/dist/rules/no-global-error-in-failure.js +54 -0
- package/dist/rules/no-globals.d.ts +6 -0
- package/dist/rules/no-globals.js +164 -0
- package/dist/rules/no-if-statement.d.ts +6 -0
- package/dist/rules/no-if-statement.js +18 -0
- package/dist/rules/no-iife-wrapper.d.ts +6 -0
- package/dist/rules/no-iife-wrapper.js +37 -0
- package/dist/rules/no-inline-provide.d.ts +6 -0
- package/dist/rules/no-inline-provide.js +31 -0
- package/dist/rules/no-instanceof-schema.d.ts +6 -0
- package/dist/rules/no-instanceof-schema.js +35 -0
- package/dist/rules/no-manual-effect-channels.d.ts +6 -0
- package/dist/rules/no-manual-effect-channels.js +40 -0
- package/dist/rules/no-match-effect-branch.d.ts +6 -0
- package/dist/rules/no-match-effect-branch.js +64 -0
- package/dist/rules/no-match-void-branch.d.ts +6 -0
- package/dist/rules/no-match-void-branch.js +50 -0
- package/dist/rules/no-multiple-effect-provide.d.ts +6 -0
- package/dist/rules/no-multiple-effect-provide.js +45 -0
- package/dist/rules/no-nested-effect-call.d.ts +6 -0
- package/dist/rules/no-nested-effect-call.js +97 -0
- package/dist/rules/no-nested-effect-gen.d.ts +6 -0
- package/dist/rules/no-nested-effect-gen.js +46 -0
- package/dist/rules/no-nested-pipe.d.ts +6 -0
- package/dist/rules/no-nested-pipe.js +38 -0
- package/dist/rules/no-new-error.d.ts +6 -0
- package/dist/rules/no-new-error.js +39 -0
- package/dist/rules/no-new-promise.d.ts +6 -0
- package/dist/rules/no-new-promise.js +38 -0
- package/dist/rules/no-node-builtin-import.d.ts +6 -0
- package/dist/rules/no-node-builtin-import.js +61 -0
- package/dist/rules/no-option-as.d.ts +6 -0
- package/dist/rules/no-option-as.js +16 -0
- package/dist/rules/no-option-boolean-normalization.d.ts +6 -0
- package/dist/rules/no-option-boolean-normalization.js +50 -0
- package/dist/rules/no-positional-log-error.d.ts +6 -0
- package/dist/rules/no-positional-log-error.js +52 -0
- package/dist/rules/no-redundant-schema-tag-identifier.d.ts +6 -0
- package/dist/rules/no-redundant-schema-tag-identifier.js +46 -0
- package/dist/rules/no-return-in-arrow.d.ts +6 -0
- package/dist/rules/no-return-in-arrow.js +41 -0
- package/dist/rules/no-return-nullish.d.ts +6 -0
- package/dist/rules/no-return-nullish.js +47 -0
- package/dist/rules/no-run-in-effect-gen.d.ts +6 -0
- package/dist/rules/no-run-in-effect-gen.js +48 -0
- package/dist/rules/no-runtime-run-fork.d.ts +6 -0
- package/dist/rules/no-runtime-run-fork.js +19 -0
- package/dist/rules/no-schema-struct-with-tag.d.ts +6 -0
- package/dist/rules/no-schema-struct-with-tag.js +48 -0
- package/dist/rules/no-schema-union-of-literals.d.ts +6 -0
- package/dist/rules/no-schema-union-of-literals.js +35 -0
- package/dist/rules/no-string-sentinel-const.d.ts +6 -0
- package/dist/rules/no-string-sentinel-const.js +38 -0
- package/dist/rules/no-switch-statement.d.ts +6 -0
- package/dist/rules/no-switch-statement.js +16 -0
- package/dist/rules/no-ternary.d.ts +6 -0
- package/dist/rules/no-ternary.js +30 -0
- package/dist/rules/no-throw-statement.d.ts +6 -0
- package/dist/rules/no-throw-statement.js +34 -0
- package/dist/rules/no-try-catch.d.ts +6 -0
- package/dist/rules/no-try-catch.js +34 -0
- package/dist/rules/no-unnecessary-arrow-block.d.ts +6 -0
- package/dist/rules/no-unnecessary-arrow-block.js +34 -0
- package/dist/rules/no-unnecessary-effect-gen.d.ts +6 -0
- package/dist/rules/no-unnecessary-effect-gen.js +35 -0
- package/dist/rules/no-unnecessary-pipe-chain.d.ts +6 -0
- package/dist/rules/no-unnecessary-pipe-chain.js +36 -0
- package/dist/rules/no-unnecessary-pipe.d.ts +6 -0
- package/dist/rules/no-unnecessary-pipe.js +29 -0
- package/dist/vendor/effect-oxlint/AST.d.ts +199 -0
- package/dist/vendor/effect-oxlint/AST.js +214 -0
- package/dist/vendor/effect-oxlint/Comment.d.ts +55 -0
- package/dist/vendor/effect-oxlint/Comment.js +67 -0
- package/dist/vendor/effect-oxlint/Diagnostic.d.ts +86 -0
- package/dist/vendor/effect-oxlint/Diagnostic.js +115 -0
- package/dist/vendor/effect-oxlint/Plugin.d.ts +38 -0
- package/dist/vendor/effect-oxlint/Plugin.js +45 -0
- package/dist/vendor/effect-oxlint/Rule.d.ts +218 -0
- package/dist/vendor/effect-oxlint/Rule.js +309 -0
- package/dist/vendor/effect-oxlint/RuleContext.d.ts +77 -0
- package/dist/vendor/effect-oxlint/RuleContext.js +74 -0
- package/dist/vendor/effect-oxlint/Scope.d.ts +100 -0
- package/dist/vendor/effect-oxlint/Scope.js +110 -0
- package/dist/vendor/effect-oxlint/SourceCode.d.ts +180 -0
- package/dist/vendor/effect-oxlint/SourceCode.js +203 -0
- package/dist/vendor/effect-oxlint/Testing.d.ts +510 -0
- package/dist/vendor/effect-oxlint/Testing.js +963 -0
- package/dist/vendor/effect-oxlint/Token.d.ts +98 -0
- package/dist/vendor/effect-oxlint/Token.js +103 -0
- package/dist/vendor/effect-oxlint/Visitor.d.ts +166 -0
- package/dist/vendor/effect-oxlint/Visitor.js +138 -0
- package/dist/vendor/effect-oxlint/index.d.ts +13 -0
- package/dist/vendor/effect-oxlint/index.js +12 -0
- package/package.json +24 -5
- package/.effect-lsp.json +0 -60
- package/.oxlintrc.json +0 -33
- package/CHANGELOG.md +0 -55
- package/scripts/add-rule.ts +0 -87
- package/scripts/codegen.ts +0 -108
- package/src/index.ts +0 -9
- package/src/plugin.ts +0 -16
- package/src/presets/core.ts +0 -47
- package/src/presets/effect-native.ts +0 -34
- package/src/presets/full.ts +0 -70
- package/src/presets/functional.ts +0 -18
- package/src/presets/index.ts +0 -5
- package/src/presets/strict.ts +0 -77
- package/src/rules/_effect-context.ts +0 -28
- package/src/rules/index.ts +0 -72
- package/src/rules/no-arrow-ladder.ts +0 -70
- package/src/rules/no-async-function.ts +0 -47
- package/src/rules/no-catch-all-to-map-error.ts +0 -90
- package/src/rules/no-effect-as.ts +0 -14
- package/src/rules/no-effect-async.ts +0 -14
- package/src/rules/no-effect-bind.ts +0 -13
- package/src/rules/no-effect-do.ts +0 -14
- package/src/rules/no-effect-fn-generator.ts +0 -58
- package/src/rules/no-effect-gen-adapter.ts +0 -51
- package/src/rules/no-effect-map-flatten.ts +0 -82
- package/src/rules/no-effect-map-void.ts +0 -80
- package/src/rules/no-effect-never.ts +0 -14
- package/src/rules/no-effect-orElse-ladder.ts +0 -79
- package/src/rules/no-effect-succeed-string.ts +0 -52
- package/src/rules/no-effect-succeed-variable.ts +0 -50
- package/src/rules/no-effect-succeed-void.ts +0 -63
- package/src/rules/no-effect-sync-console.ts +0 -91
- package/src/rules/no-effect-sync-wrapper.ts +0 -67
- package/src/rules/no-effect-type-alias.ts +0 -71
- package/src/rules/no-effect-wrapper-alias.ts +0 -95
- package/src/rules/no-extends-native-error.ts +0 -54
- package/src/rules/no-flatmap-ladder.ts +0 -77
- package/src/rules/no-fromnullable-coalesce.ts +0 -62
- package/src/rules/no-global-error-in-catch.ts +0 -104
- package/src/rules/no-global-error-in-failure.ts +0 -83
- package/src/rules/no-globals.ts +0 -178
- package/src/rules/no-if-statement.ts +0 -16
- package/src/rules/no-iife-wrapper.ts +0 -51
- package/src/rules/no-inline-provide.ts +0 -41
- package/src/rules/no-instanceof-schema.ts +0 -42
- package/src/rules/no-manual-effect-channels.ts +0 -58
- package/src/rules/no-match-effect-branch.ts +0 -82
- package/src/rules/no-match-void-branch.ts +0 -72
- package/src/rules/no-multiple-effect-provide.ts +0 -75
- package/src/rules/no-nested-effect-call.ts +0 -57
- package/src/rules/no-nested-effect-gen.ts +0 -43
- package/src/rules/no-nested-pipe.ts +0 -73
- package/src/rules/no-new-error.ts +0 -53
- package/src/rules/no-new-promise.ts +0 -50
- package/src/rules/no-node-builtin-import.ts +0 -68
- package/src/rules/no-option-as.ts +0 -13
- package/src/rules/no-option-boolean-normalization.ts +0 -88
- package/src/rules/no-positional-log-error.ts +0 -76
- package/src/rules/no-redundant-schema-tag-identifier.ts +0 -62
- package/src/rules/no-return-in-arrow.ts +0 -76
- package/src/rules/no-return-nullish.ts +0 -80
- package/src/rules/no-run-in-effect-gen.ts +0 -66
- package/src/rules/no-runtime-run-fork.ts +0 -14
- package/src/rules/no-schema-struct-with-tag.ts +0 -64
- package/src/rules/no-schema-union-of-literals.ts +0 -48
- package/src/rules/no-string-sentinel-const.ts +0 -49
- package/src/rules/no-switch-statement.ts +0 -13
- package/src/rules/no-ternary.ts +0 -30
- package/src/rules/no-throw-statement.ts +0 -39
- package/src/rules/no-try-catch.ts +0 -39
- package/src/rules/no-unnecessary-arrow-block.ts +0 -51
- package/src/rules/no-unnecessary-effect-gen.ts +0 -83
- package/src/rules/no-unnecessary-pipe-chain.ts +0 -47
- package/src/rules/no-unnecessary-pipe.ts +0 -45
- package/src/vendor/effect-oxlint/AST.ts +0 -516
- package/src/vendor/effect-oxlint/Comment.ts +0 -86
- package/src/vendor/effect-oxlint/Diagnostic.ts +0 -199
- package/src/vendor/effect-oxlint/Plugin.ts +0 -71
- package/src/vendor/effect-oxlint/Rule.ts +0 -548
- package/src/vendor/effect-oxlint/RuleContext.ts +0 -144
- package/src/vendor/effect-oxlint/Scope.ts +0 -177
- package/src/vendor/effect-oxlint/SourceCode.ts +0 -377
- package/src/vendor/effect-oxlint/Testing.ts +0 -1227
- package/src/vendor/effect-oxlint/Token.ts +0 -131
- package/src/vendor/effect-oxlint/Visitor.ts +0 -287
- package/src/vendor/effect-oxlint/index.ts +0 -88
- package/tests/ban-rules.test.ts +0 -68
- package/tests/context-rules.test.ts +0 -263
- package/tests/pattern-rules.test.ts +0 -247
- package/tsconfig.json +0 -15
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban nested IIFE chains — `((x) => ((y) => ...)(arg2))(arg1)`.
|
|
3
|
-
*
|
|
4
|
-
* Use named const bindings + flat pipeline.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-arrow-ladder
|
|
7
|
-
*/
|
|
8
|
-
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import * as Effect from "effect/Effect"
|
|
10
|
-
|
|
11
|
-
const isIife = (node: unknown): boolean => {
|
|
12
|
-
if (node == null || typeof node !== "object") return false
|
|
13
|
-
const n = node as unknown as Record<string, unknown>
|
|
14
|
-
if (n["type"] !== "CallExpression") return false
|
|
15
|
-
const callee = n["callee"]
|
|
16
|
-
if (callee == null || typeof callee !== "object") return false
|
|
17
|
-
const c = callee as unknown as Record<string, unknown>
|
|
18
|
-
return c["type"] === "ArrowFunctionExpression" || c["type"] === "FunctionExpression"
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const containsIife = (node: unknown): boolean => {
|
|
22
|
-
if (node == null || typeof node !== "object") return false
|
|
23
|
-
const n = node as unknown as Record<string, unknown>
|
|
24
|
-
if (isIife(n)) return true
|
|
25
|
-
if ("body" in n) {
|
|
26
|
-
if (Array.isArray(n["body"])) {
|
|
27
|
-
for (const child of n["body"]) {
|
|
28
|
-
if (containsIife(child)) return true
|
|
29
|
-
}
|
|
30
|
-
} else if (containsIife(n["body"])) return true
|
|
31
|
-
}
|
|
32
|
-
if ("argument" in n && containsIife(n["argument"])) return true
|
|
33
|
-
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
34
|
-
for (const arg of n["arguments"]) {
|
|
35
|
-
if (containsIife(arg)) return true
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if ("expression" in n && containsIife(n["expression"])) return true
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export const noArrowLadder = Rule.define({
|
|
43
|
-
name: "no-arrow-ladder",
|
|
44
|
-
meta: Rule.meta({
|
|
45
|
-
type: "suggestion",
|
|
46
|
-
description: "Avoid nested IIFE chains. Use named const bindings + flat pipeline.",
|
|
47
|
-
}),
|
|
48
|
-
create: function* () {
|
|
49
|
-
const ctx = yield* RuleContext
|
|
50
|
-
return {
|
|
51
|
-
CallExpression: (node) => {
|
|
52
|
-
if (!isIife(node)) return Effect.void
|
|
53
|
-
|
|
54
|
-
// Check if the body of the IIFE itself contains another IIFE
|
|
55
|
-
const callee = (node as unknown as Record<string, unknown>)["callee"]
|
|
56
|
-
if (callee == null || typeof callee !== "object") return Effect.void
|
|
57
|
-
const c = callee as unknown as Record<string, unknown>
|
|
58
|
-
if ("body" in c && containsIife(c["body"])) {
|
|
59
|
-
return ctx.report(
|
|
60
|
-
Diagnostic.make({
|
|
61
|
-
node,
|
|
62
|
-
message: "Nested IIFE chain detected. Use named const bindings + flat pipeline.",
|
|
63
|
-
}),
|
|
64
|
-
)
|
|
65
|
-
}
|
|
66
|
-
return Effect.void
|
|
67
|
-
},
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `async` functions.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect.promise, Effect.tryPromise, or Effect.gen instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/asyncFunction
|
|
7
|
-
*
|
|
8
|
-
* Note: This is an effectNative rule — off by default.
|
|
9
|
-
* Only enable in codebases fully committed to Effect.
|
|
10
|
-
*/
|
|
11
|
-
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
12
|
-
import * as Effect from "effect/Effect"
|
|
13
|
-
|
|
14
|
-
export const noAsyncFunction = Rule.define({
|
|
15
|
-
name: "no-async-function",
|
|
16
|
-
meta: Rule.meta({
|
|
17
|
-
type: "suggestion",
|
|
18
|
-
description:
|
|
19
|
-
"Avoid async functions. Use Effect.promise or Effect.tryPromise.",
|
|
20
|
-
}),
|
|
21
|
-
create: function* () {
|
|
22
|
-
const ctx = yield* RuleContext
|
|
23
|
-
const report = (node: Parameters<typeof Diagnostic.make>[0]["node"]) =>
|
|
24
|
-
ctx.report(
|
|
25
|
-
Diagnostic.make({
|
|
26
|
-
node,
|
|
27
|
-
message:
|
|
28
|
-
"Avoid async functions. Use Effect.promise, Effect.tryPromise, or Effect.gen.",
|
|
29
|
-
}),
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
FunctionDeclaration: (node) => {
|
|
34
|
-
if ("async" in node && node.async === true) return report(node)
|
|
35
|
-
return Effect.void
|
|
36
|
-
},
|
|
37
|
-
FunctionExpression: (node) => {
|
|
38
|
-
if ("async" in node && node.async === true) return report(node)
|
|
39
|
-
return Effect.void
|
|
40
|
-
},
|
|
41
|
-
ArrowFunctionExpression: (node) => {
|
|
42
|
-
if ("async" in node && node.async === true) return report(node)
|
|
43
|
-
return Effect.void
|
|
44
|
-
},
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
})
|
|
@@ -1,90 +0,0 @@
|
|
|
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
|
-
})
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.as`.
|
|
3
|
-
*
|
|
4
|
-
* Use `Effect.map` for value mapping or `Effect.asVoid` after explicit steps.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-as
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noEffectAs = Rule.banMember("Effect", "as", {
|
|
11
|
-
message:
|
|
12
|
-
"Avoid Effect.as. Use Effect.map for value mapping or Effect.asVoid.",
|
|
13
|
-
meta: { type: "suggestion" },
|
|
14
|
-
})
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.async`.
|
|
3
|
-
*
|
|
4
|
-
* Use `Stream` or structured Effect lifecycles (acquire/use/release).
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-async
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noEffectAsync = Rule.banMember("Effect", "async", {
|
|
11
|
-
message:
|
|
12
|
-
"Avoid Effect.async. Use Stream or structured Effect lifecycles.",
|
|
13
|
-
meta: { type: "suggestion" },
|
|
14
|
-
})
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.bind`.
|
|
3
|
-
*
|
|
4
|
-
* Use flat pipe or direct top-level `Effect.gen` with yields.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-bind
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noEffectBind = Rule.banMember("Effect", "bind", {
|
|
11
|
-
message: "Avoid Effect.bind. Use flat pipe or Effect.gen with yields.",
|
|
12
|
-
meta: { type: "suggestion" },
|
|
13
|
-
})
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.Do` — the builder/do-notation entry point.
|
|
3
|
-
*
|
|
4
|
-
* Use flat pipe-based flow or `Effect.gen` instead.
|
|
5
|
-
*
|
|
6
|
-
* Sources: biome-effect-linting-rules/no-effect-do, language-service/effectDoNotation
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noEffectDo = Rule.banMember("Effect", "Do", {
|
|
11
|
-
message:
|
|
12
|
-
"Avoid Effect.Do builder notation. Use flat pipe-based flow or Effect.gen.",
|
|
13
|
-
meta: { type: "suggestion" },
|
|
14
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.fn(function* () { ... })` called with a generator argument.
|
|
3
|
-
*
|
|
4
|
-
* Effect.fn should wrap a plain function or arrow that builds a pipeline,
|
|
5
|
-
* not a generator (use Effect.gen for generators).
|
|
6
|
-
*
|
|
7
|
-
* Source: biome-effect-linting-rules/no-effect-fn-generator
|
|
8
|
-
*
|
|
9
|
-
* Note: This rule may be too aggressive. In Effect v4, `Effect.fn("name")(function*() {...})`
|
|
10
|
-
* is actually the idiomatic pattern. This rule targets `Effect.fn(function*() {...})` without
|
|
11
|
-
* a name span, which is the discouraged pattern from the biome rules.
|
|
12
|
-
*/
|
|
13
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
14
|
-
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
15
|
-
import * as Effect from "effect/Effect"
|
|
16
|
-
|
|
17
|
-
export const noEffectFnGenerator = Rule.define({
|
|
18
|
-
name: "no-effect-fn-generator",
|
|
19
|
-
meta: Rule.meta({
|
|
20
|
-
type: "suggestion",
|
|
21
|
-
description:
|
|
22
|
-
"Effect.fn should not directly wrap a generator. Use Effect.fn('name')(function*() {...}) with a span name.",
|
|
23
|
-
}),
|
|
24
|
-
create: function* () {
|
|
25
|
-
const ctx = yield* RuleContext
|
|
26
|
-
return {
|
|
27
|
-
CallExpression: (node) => {
|
|
28
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "fn")) return Effect.void
|
|
29
|
-
|
|
30
|
-
// Effect.fn(function*() {...}) — single arg that's a generator
|
|
31
|
-
if (
|
|
32
|
-
"arguments" in node &&
|
|
33
|
-
Array.isArray(node.arguments) &&
|
|
34
|
-
node.arguments.length === 1
|
|
35
|
-
) {
|
|
36
|
-
const arg = node.arguments[0]
|
|
37
|
-
if (
|
|
38
|
-
arg != null &&
|
|
39
|
-
typeof arg === "object" &&
|
|
40
|
-
"type" in arg &&
|
|
41
|
-
arg.type === "FunctionExpression" &&
|
|
42
|
-
"generator" in arg &&
|
|
43
|
-
arg.generator === true
|
|
44
|
-
) {
|
|
45
|
-
return ctx.report(
|
|
46
|
-
Diagnostic.make({
|
|
47
|
-
node,
|
|
48
|
-
message:
|
|
49
|
-
"Use Effect.fn('spanName')(function*() {...}) — provide a trace span name.",
|
|
50
|
-
}),
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return Effect.void
|
|
55
|
-
},
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban deprecated adapter parameter in `Effect.gen(function*(adapter) {...})`.
|
|
3
|
-
*
|
|
4
|
-
* The adapter parameter is deprecated — just use `yield*` directly.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/effectGenUsesAdapter
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import * as Effect from "effect/Effect"
|
|
11
|
-
|
|
12
|
-
export const noEffectGenAdapter = Rule.define({
|
|
13
|
-
name: "no-effect-gen-adapter",
|
|
14
|
-
meta: Rule.meta({
|
|
15
|
-
type: "suggestion",
|
|
16
|
-
description: "Deprecated adapter parameter in Effect.gen. Use `yield*` directly.",
|
|
17
|
-
effectVersion: "v3",
|
|
18
|
-
}),
|
|
19
|
-
create: function* () {
|
|
20
|
-
const ctx = yield* RuleContext
|
|
21
|
-
return {
|
|
22
|
-
CallExpression: (node) => {
|
|
23
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "gen")) return Effect.void
|
|
24
|
-
|
|
25
|
-
if (!("arguments" in node) || !Array.isArray(node.arguments) || node.arguments.length < 1)
|
|
26
|
-
return Effect.void
|
|
27
|
-
|
|
28
|
-
const arg = node.arguments[0]
|
|
29
|
-
if (
|
|
30
|
-
arg != null &&
|
|
31
|
-
typeof arg === "object" &&
|
|
32
|
-
"type" in arg &&
|
|
33
|
-
arg.type === "FunctionExpression" &&
|
|
34
|
-
"generator" in arg &&
|
|
35
|
-
arg.generator === true &&
|
|
36
|
-
"params" in arg &&
|
|
37
|
-
Array.isArray(arg.params) &&
|
|
38
|
-
arg.params.length > 0
|
|
39
|
-
) {
|
|
40
|
-
return ctx.report(
|
|
41
|
-
Diagnostic.make({
|
|
42
|
-
node,
|
|
43
|
-
message: "Deprecated adapter parameter in Effect.gen. Remove the parameter and use `yield*` directly.",
|
|
44
|
-
}),
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
return Effect.void
|
|
48
|
-
},
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
})
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `.map(f).flatMap(g)` — use `.flatMap(x => g(f(x)))` or refactor.
|
|
3
|
-
*
|
|
4
|
-
* Detects `.pipe(Effect.map(f), Effect.flatMap(g))` chains where
|
|
5
|
-
* the map + flatMap can be fused.
|
|
6
|
-
*
|
|
7
|
-
* Source: language-service/effectMapFlatten
|
|
8
|
-
*/
|
|
9
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
10
|
-
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
|
|
14
|
-
const isEffectMap = (node: ESTree.Node): boolean => {
|
|
15
|
-
if (node.type !== "CallExpression") return false
|
|
16
|
-
return Option.isSome(AST.matchCallOf(node as ESTree.CallExpression, "Effect", "map"))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const isEffectFlatMap = (node: ESTree.Node): boolean => {
|
|
20
|
-
if (node.type !== "CallExpression") return false
|
|
21
|
-
return Option.isSome(AST.matchCallOf(node as ESTree.CallExpression, "Effect", "flatMap"))
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const hasMapFlatMapSequence = (args: ReadonlyArray<ESTree.Node>): boolean => {
|
|
25
|
-
for (let i = 0; i < args.length - 1; i++) {
|
|
26
|
-
const a = args[i]; const b = args[i + 1]; if (a && b && isEffectMap(a) && isEffectFlatMap(b)) return true
|
|
27
|
-
}
|
|
28
|
-
return false
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const noEffectMapFlatten = Rule.define({
|
|
32
|
-
name: "no-effect-map-flatten",
|
|
33
|
-
meta: Rule.meta({
|
|
34
|
-
type: "suggestion",
|
|
35
|
-
description:
|
|
36
|
-
"Avoid Effect.map followed by Effect.flatMap in pipe. Fuse into a single Effect.flatMap.",
|
|
37
|
-
}),
|
|
38
|
-
create: function* () {
|
|
39
|
-
const ctx = yield* RuleContext
|
|
40
|
-
return {
|
|
41
|
-
CallExpression: (node) => {
|
|
42
|
-
const call = node as ESTree.CallExpression
|
|
43
|
-
const args = call.arguments
|
|
44
|
-
|
|
45
|
-
// Bare pipe(value, Effect.map(f), Effect.flatMap(g))
|
|
46
|
-
const calleeName = Option.getOrUndefined(AST.calleeName(call))
|
|
47
|
-
if (calleeName === "pipe" && hasMapFlatMapSequence(args as ReadonlyArray<ESTree.Node>)) {
|
|
48
|
-
return ctx.report(
|
|
49
|
-
Diagnostic.make({
|
|
50
|
-
node,
|
|
51
|
-
message:
|
|
52
|
-
"Effect.map followed by Effect.flatMap can be fused into a single Effect.flatMap.",
|
|
53
|
-
}),
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// .pipe(Effect.map(f), Effect.flatMap(g))
|
|
58
|
-
const calleeNode = call.callee
|
|
59
|
-
if (calleeNode.type === "MemberExpression") {
|
|
60
|
-
const prop = (calleeNode as unknown as Record<string, unknown>)["property"]
|
|
61
|
-
if (
|
|
62
|
-
prop != null &&
|
|
63
|
-
typeof prop === "object" &&
|
|
64
|
-
"name" in prop &&
|
|
65
|
-
prop.name === "pipe" &&
|
|
66
|
-
hasMapFlatMapSequence(args as ReadonlyArray<ESTree.Node>)
|
|
67
|
-
) {
|
|
68
|
-
return ctx.report(
|
|
69
|
-
Diagnostic.make({
|
|
70
|
-
node,
|
|
71
|
-
message:
|
|
72
|
-
"Effect.map followed by Effect.flatMap can be fused into a single Effect.flatMap.",
|
|
73
|
-
}),
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return Effect.void
|
|
79
|
-
},
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
})
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.map(() => undefined)` / `Effect.map(() => void 0)` / `Effect.map(() => {})`.
|
|
3
|
-
*
|
|
4
|
-
* Use `Effect.asVoid` instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/effectMapVoid
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import * as Effect from "effect/Effect"
|
|
11
|
-
|
|
12
|
-
const isVoidCallback = (arg: unknown): boolean => {
|
|
13
|
-
if (arg == null || typeof arg !== "object" || !("type" in arg)) return false
|
|
14
|
-
|
|
15
|
-
// Arrow function: () => undefined, () => void 0, () => {}
|
|
16
|
-
if (arg.type === "ArrowFunctionExpression" && "body" in arg) {
|
|
17
|
-
const body = arg.body
|
|
18
|
-
if (body == null || typeof body !== "object" || !("type" in body)) return false
|
|
19
|
-
|
|
20
|
-
// () => undefined
|
|
21
|
-
if (body.type === "Identifier" && "name" in body && body.name === "undefined") return true
|
|
22
|
-
|
|
23
|
-
// () => void 0
|
|
24
|
-
if (
|
|
25
|
-
body.type === "UnaryExpression" &&
|
|
26
|
-
"operator" in body &&
|
|
27
|
-
body.operator === "void"
|
|
28
|
-
)
|
|
29
|
-
return true
|
|
30
|
-
|
|
31
|
-
// () => {} (empty block)
|
|
32
|
-
if (
|
|
33
|
-
body.type === "BlockStatement" &&
|
|
34
|
-
"body" in body &&
|
|
35
|
-
Array.isArray(body.body) &&
|
|
36
|
-
body.body.length === 0
|
|
37
|
-
)
|
|
38
|
-
return true
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const noEffectMapVoid = Rule.define({
|
|
45
|
-
name: "no-effect-map-void",
|
|
46
|
-
meta: Rule.meta({
|
|
47
|
-
type: "suggestion",
|
|
48
|
-
description:
|
|
49
|
-
"Avoid Effect.map(() => undefined). Use Effect.asVoid instead.",
|
|
50
|
-
}),
|
|
51
|
-
create: function* () {
|
|
52
|
-
const ctx = yield* RuleContext
|
|
53
|
-
return {
|
|
54
|
-
CallExpression: (node) => {
|
|
55
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "map")) return Effect.void
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
"arguments" in node &&
|
|
59
|
-
Array.isArray(node.arguments) &&
|
|
60
|
-
node.arguments.length >= 1
|
|
61
|
-
) {
|
|
62
|
-
// Effect.map(effect, fn) or as pipe arg Effect.map(fn)
|
|
63
|
-
const fn =
|
|
64
|
-
node.arguments.length === 2
|
|
65
|
-
? node.arguments[1]
|
|
66
|
-
: node.arguments[0]
|
|
67
|
-
if (isVoidCallback(fn)) {
|
|
68
|
-
return ctx.report(
|
|
69
|
-
Diagnostic.make({
|
|
70
|
-
node,
|
|
71
|
-
message: "Use Effect.asVoid instead of Effect.map(() => undefined).",
|
|
72
|
-
}),
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return Effect.void
|
|
77
|
-
},
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
})
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.never`.
|
|
3
|
-
*
|
|
4
|
-
* Use `Stream` or explicit acquire/release lifecycles instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-never
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noEffectNever = Rule.banMember("Effect", "never", {
|
|
11
|
-
message:
|
|
12
|
-
"Avoid Effect.never. Use Stream or explicit acquire/release lifecycles.",
|
|
13
|
-
meta: { type: "suggestion" },
|
|
14
|
-
})
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.orElse` wrapping chains with sequencing combinators.
|
|
3
|
-
*
|
|
4
|
-
* Move error handling to a single terminal decision after the pipeline.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-orElse-ladder
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import * as Effect from "effect/Effect"
|
|
11
|
-
|
|
12
|
-
const sequencingCombinators = ["flatMap", "zipRight", "as", "tap", "andThen"]
|
|
13
|
-
|
|
14
|
-
const containsSequencing = (node: unknown): boolean => {
|
|
15
|
-
if (node == null || typeof node !== "object") return false
|
|
16
|
-
const n = node as Record<string, unknown>
|
|
17
|
-
if (n["type"] === "CallExpression" && "callee" in n) {
|
|
18
|
-
const callee = n["callee"] as Record<string, unknown> | null
|
|
19
|
-
if (
|
|
20
|
-
callee != null &&
|
|
21
|
-
callee["type"] === "MemberExpression" &&
|
|
22
|
-
"object" in callee &&
|
|
23
|
-
"property" in callee
|
|
24
|
-
) {
|
|
25
|
-
const obj = callee["object"] as Record<string, unknown> | null
|
|
26
|
-
const prop = callee["property"] as Record<string, unknown> | null
|
|
27
|
-
if (
|
|
28
|
-
obj != null &&
|
|
29
|
-
obj["type"] === "Identifier" &&
|
|
30
|
-
obj["name"] === "Effect" &&
|
|
31
|
-
prop != null &&
|
|
32
|
-
prop["type"] === "Identifier" &&
|
|
33
|
-
typeof prop["name"] === "string" &&
|
|
34
|
-
sequencingCombinators.includes(prop["name"])
|
|
35
|
-
) {
|
|
36
|
-
return true
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
40
|
-
for (const arg of n["arguments"]) {
|
|
41
|
-
if (containsSequencing(arg)) return true
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if ("body" in n) return containsSequencing(n["body"])
|
|
46
|
-
return false
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const noEffectOrElseLadder = Rule.define({
|
|
50
|
-
name: "no-effect-orElse-ladder",
|
|
51
|
-
meta: Rule.meta({
|
|
52
|
-
type: "suggestion",
|
|
53
|
-
description:
|
|
54
|
-
"Avoid Effect.orElse wrapping sequencing chains. Handle errors at a single terminal decision.",
|
|
55
|
-
}),
|
|
56
|
-
create: function* () {
|
|
57
|
-
const ctx = yield* RuleContext
|
|
58
|
-
return {
|
|
59
|
-
CallExpression: (node) => {
|
|
60
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "orElse")) return Effect.void
|
|
61
|
-
|
|
62
|
-
if ("arguments" in node && Array.isArray(node.arguments)) {
|
|
63
|
-
for (const arg of node.arguments) {
|
|
64
|
-
if (containsSequencing(arg)) {
|
|
65
|
-
return ctx.report(
|
|
66
|
-
Diagnostic.make({
|
|
67
|
-
node,
|
|
68
|
-
message:
|
|
69
|
-
"Effect.orElse wrapping sequencing chain. Move error handling to a terminal decision after the pipeline.",
|
|
70
|
-
}),
|
|
71
|
-
)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return Effect.void
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
})
|