oxlint-plugin-effect 0.2.0 → 0.2.1
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 +13 -0
- package/dist/presets/index.js +18 -0
- package/dist/presets/strict.d.ts +69 -0
- package/dist/presets/strict.js +69 -0
- package/dist/presets/v3.d.ts +15 -0
- package/dist/presets/v3.js +15 -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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-map-void.ts
|
|
8
|
+
const isVoidCallback = (arg) => {
|
|
9
|
+
if (arg == null || typeof arg !== "object" || !("type" in arg)) return false;
|
|
10
|
+
if (arg.type === "ArrowFunctionExpression" && "body" in arg) {
|
|
11
|
+
const body = arg.body;
|
|
12
|
+
if (body == null || typeof body !== "object" || !("type" in body)) return false;
|
|
13
|
+
if (body.type === "Identifier" && "name" in body && body.name === "undefined") return true;
|
|
14
|
+
if (body.type === "UnaryExpression" && "operator" in body && body.operator === "void") return true;
|
|
15
|
+
if (body.type === "BlockStatement" && "body" in body && Array.isArray(body.body) && body.body.length === 0) return true;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
};
|
|
19
|
+
const noEffectMapVoid = define({
|
|
20
|
+
name: "no-effect-map-void",
|
|
21
|
+
meta: meta({
|
|
22
|
+
type: "suggestion",
|
|
23
|
+
description: "Avoid Effect.map(() => undefined). Use Effect.asVoid instead."
|
|
24
|
+
}),
|
|
25
|
+
create: function* () {
|
|
26
|
+
const ctx = yield* RuleContext;
|
|
27
|
+
return { CallExpression: (node) => {
|
|
28
|
+
if (!isCallOf(node, "Effect", "map")) return Effect.void;
|
|
29
|
+
if ("arguments" in node && Array.isArray(node.arguments) && node.arguments.length >= 1) {
|
|
30
|
+
if (isVoidCallback(node.arguments.length === 2 ? node.arguments[1] : node.arguments[0])) return ctx.report(make({
|
|
31
|
+
node,
|
|
32
|
+
message: "Use Effect.asVoid instead of Effect.map(() => undefined)."
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
return Effect.void;
|
|
36
|
+
} };
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
//#endregion
|
|
40
|
+
export { noEffectMapVoid };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { banMember } from "../vendor/effect-oxlint/Rule.js";
|
|
2
|
+
import "../vendor/effect-oxlint/index.js";
|
|
3
|
+
//#region src/rules/no-effect-never.ts
|
|
4
|
+
/**
|
|
5
|
+
* Ban `Effect.never`.
|
|
6
|
+
*
|
|
7
|
+
* Use `Stream` or explicit acquire/release lifecycles instead.
|
|
8
|
+
*
|
|
9
|
+
* Source: biome-effect-linting-rules/no-effect-never
|
|
10
|
+
*/
|
|
11
|
+
const noEffectNever = banMember("Effect", "never", {
|
|
12
|
+
message: "Avoid Effect.never. Use Stream or explicit acquire/release lifecycles.",
|
|
13
|
+
meta: { type: "suggestion" }
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { noEffectNever };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-orElse-ladder.ts
|
|
8
|
+
const sequencingCombinators = [
|
|
9
|
+
"flatMap",
|
|
10
|
+
"zipRight",
|
|
11
|
+
"as",
|
|
12
|
+
"tap",
|
|
13
|
+
"andThen"
|
|
14
|
+
];
|
|
15
|
+
const containsSequencing = (node) => {
|
|
16
|
+
if (node == null || typeof node !== "object") return false;
|
|
17
|
+
const n = node;
|
|
18
|
+
if (n["type"] === "CallExpression" && "callee" in n) {
|
|
19
|
+
const callee = n["callee"];
|
|
20
|
+
if (callee != null && callee["type"] === "MemberExpression" && "object" in callee && "property" in callee) {
|
|
21
|
+
const obj = callee["object"];
|
|
22
|
+
const prop = callee["property"];
|
|
23
|
+
if (obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" && prop != null && prop["type"] === "Identifier" && typeof prop["name"] === "string" && sequencingCombinators.includes(prop["name"])) return true;
|
|
24
|
+
}
|
|
25
|
+
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
26
|
+
for (const arg of n["arguments"]) if (containsSequencing(arg)) return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if ("body" in n) return containsSequencing(n["body"]);
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
const noEffectOrElseLadder = define({
|
|
33
|
+
name: "no-effect-orElse-ladder",
|
|
34
|
+
meta: meta({
|
|
35
|
+
type: "suggestion",
|
|
36
|
+
description: "Avoid Effect.orElse wrapping sequencing chains. Handle errors at a single terminal decision."
|
|
37
|
+
}),
|
|
38
|
+
create: function* () {
|
|
39
|
+
const ctx = yield* RuleContext;
|
|
40
|
+
return { CallExpression: (node) => {
|
|
41
|
+
if (!isCallOf(node, "Effect", "orElse")) return Effect.void;
|
|
42
|
+
if ("arguments" in node && Array.isArray(node.arguments)) {
|
|
43
|
+
for (const arg of node.arguments) if (containsSequencing(arg)) return ctx.report(make({
|
|
44
|
+
node,
|
|
45
|
+
message: "Effect.orElse wrapping sequencing chain. Move error handling to a terminal decision after the pipeline."
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
return Effect.void;
|
|
49
|
+
} };
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
//#endregion
|
|
53
|
+
export { noEffectOrElseLadder };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-succeed-string.ts
|
|
8
|
+
const noEffectSucceedString = define({
|
|
9
|
+
name: "no-effect-succeed-string",
|
|
10
|
+
meta: meta({
|
|
11
|
+
type: "suggestion",
|
|
12
|
+
description: "Avoid Effect.succeed with string literals. Return domain values (tagged unions, Option, Either)."
|
|
13
|
+
}),
|
|
14
|
+
create: function* () {
|
|
15
|
+
const ctx = yield* RuleContext;
|
|
16
|
+
return { CallExpression: (node) => {
|
|
17
|
+
if (!isCallOf(node, "Effect", "succeed")) return Effect.void;
|
|
18
|
+
if ("arguments" in node && Array.isArray(node.arguments) && node.arguments.length === 1) {
|
|
19
|
+
const arg = node.arguments[0];
|
|
20
|
+
if (arg != null && typeof arg === "object" && "type" in arg && arg.type === "Literal" && "value" in arg && typeof arg.value === "string") return ctx.report(make({
|
|
21
|
+
node,
|
|
22
|
+
message: "Avoid Effect.succeed with string literals. Use tagged unions, Option, or Either."
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
return Effect.void;
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
//#endregion
|
|
30
|
+
export { noEffectSucceedString };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-succeed-variable.ts
|
|
8
|
+
const noEffectSucceedVariable = define({
|
|
9
|
+
name: "no-effect-succeed-variable",
|
|
10
|
+
meta: meta({
|
|
11
|
+
type: "suggestion",
|
|
12
|
+
description: "Avoid Effect.succeed with a plain variable. Select value with Option/Match, then run one Effect pipeline."
|
|
13
|
+
}),
|
|
14
|
+
create: function* () {
|
|
15
|
+
const ctx = yield* RuleContext;
|
|
16
|
+
return { CallExpression: (node) => {
|
|
17
|
+
if (!isCallOf(node, "Effect", "succeed")) return Effect.void;
|
|
18
|
+
if ("arguments" in node && Array.isArray(node.arguments) && node.arguments.length === 1) {
|
|
19
|
+
const arg = node.arguments[0];
|
|
20
|
+
if (arg != null && typeof arg === "object" && "type" in arg && arg.type === "Identifier") return ctx.report(make({
|
|
21
|
+
node,
|
|
22
|
+
message: "Avoid Effect.succeed(variable). Select value with Option/Match, then run one Effect pipeline."
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
return Effect.void;
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
//#endregion
|
|
30
|
+
export { noEffectSucceedVariable };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-succeed-void.ts
|
|
8
|
+
const isUndefinedLike = (node) => {
|
|
9
|
+
if (node == null || typeof node !== "object" || !("type" in node)) return false;
|
|
10
|
+
if (node.type === "Identifier" && "name" in node && node.name === "undefined") return true;
|
|
11
|
+
if (node.type === "UnaryExpression" && "operator" in node && node.operator === "void" && "argument" in node && node.argument != null && typeof node.argument === "object" && "type" in node.argument && node.argument.type === "Literal" && "value" in node.argument && node.argument.value === 0) return true;
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
14
|
+
const noEffectSucceedVoid = define({
|
|
15
|
+
name: "no-effect-succeed-void",
|
|
16
|
+
meta: meta({
|
|
17
|
+
type: "suggestion",
|
|
18
|
+
description: "Avoid Effect.succeed(undefined). Use Effect.void instead."
|
|
19
|
+
}),
|
|
20
|
+
create: function* () {
|
|
21
|
+
const ctx = yield* RuleContext;
|
|
22
|
+
return { CallExpression: (node) => {
|
|
23
|
+
if (!isCallOf(node, "Effect", "succeed")) return Effect.void;
|
|
24
|
+
if ("arguments" in node && Array.isArray(node.arguments) && node.arguments.length === 1 && isUndefinedLike(node.arguments[0])) return ctx.report(make({
|
|
25
|
+
node,
|
|
26
|
+
message: "Use Effect.void instead of Effect.succeed(undefined)."
|
|
27
|
+
}));
|
|
28
|
+
return Effect.void;
|
|
29
|
+
} };
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
//#endregion
|
|
33
|
+
export { noEffectSucceedVoid };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-sync-console.ts
|
|
8
|
+
const consoleMethods = new Set([
|
|
9
|
+
"log",
|
|
10
|
+
"warn",
|
|
11
|
+
"error",
|
|
12
|
+
"info",
|
|
13
|
+
"debug",
|
|
14
|
+
"trace"
|
|
15
|
+
]);
|
|
16
|
+
const containsConsoleCall = (node) => {
|
|
17
|
+
if (node == null || typeof node !== "object") return false;
|
|
18
|
+
const n = node;
|
|
19
|
+
if (n["type"] === "CallExpression" && "callee" in n && n["callee"] != null && typeof n["callee"] === "object") {
|
|
20
|
+
const callee = n["callee"];
|
|
21
|
+
if (callee["type"] === "MemberExpression" && "object" in callee && "property" in callee) {
|
|
22
|
+
const obj = callee["object"];
|
|
23
|
+
const prop = callee["property"];
|
|
24
|
+
if (obj != null && obj["type"] === "Identifier" && obj["name"] === "console" && prop != null && prop["type"] === "Identifier" && typeof prop["name"] === "string" && consoleMethods.has(prop["name"])) return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if ("body" in n) {
|
|
28
|
+
if (Array.isArray(n["body"])) {
|
|
29
|
+
for (const child of n["body"]) if (containsConsoleCall(child)) return true;
|
|
30
|
+
} else if (containsConsoleCall(n["body"])) return true;
|
|
31
|
+
}
|
|
32
|
+
if ("argument" in n && containsConsoleCall(n["argument"])) return true;
|
|
33
|
+
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
34
|
+
for (const arg of n["arguments"]) if (containsConsoleCall(arg)) return true;
|
|
35
|
+
}
|
|
36
|
+
if ("expression" in n && containsConsoleCall(n["expression"])) return true;
|
|
37
|
+
if ("expressions" in n && Array.isArray(n["expressions"])) {
|
|
38
|
+
for (const expr of n["expressions"]) if (containsConsoleCall(expr)) return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
};
|
|
42
|
+
const noEffectSyncConsole = define({
|
|
43
|
+
name: "no-effect-sync-console",
|
|
44
|
+
meta: meta({
|
|
45
|
+
type: "suggestion",
|
|
46
|
+
description: "Avoid console.* inside Effect.sync. Use Effect.log* instead."
|
|
47
|
+
}),
|
|
48
|
+
create: function* () {
|
|
49
|
+
const ctx = yield* RuleContext;
|
|
50
|
+
return { CallExpression: (node) => {
|
|
51
|
+
if (!isCallOf(node, "Effect", "sync")) return Effect.void;
|
|
52
|
+
if ("arguments" in node && Array.isArray(node.arguments)) {
|
|
53
|
+
for (const arg of node.arguments) if (containsConsoleCall(arg)) return ctx.report(make({
|
|
54
|
+
node,
|
|
55
|
+
message: "Avoid console.* inside Effect.sync. Use Effect.log, Effect.logWarning, etc."
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
return Effect.void;
|
|
59
|
+
} };
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
//#endregion
|
|
63
|
+
export { noEffectSyncConsole };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-effect-sync-wrapper.ts
|
|
8
|
+
const noEffectSyncWrapper = define({
|
|
9
|
+
name: "no-effect-sync-wrapper",
|
|
10
|
+
meta: meta({
|
|
11
|
+
type: "suggestion",
|
|
12
|
+
description: "Effect.sync wrapping a plain call. Consider if Effect.sync is necessary."
|
|
13
|
+
}),
|
|
14
|
+
create: function* () {
|
|
15
|
+
const ctx = yield* RuleContext;
|
|
16
|
+
return { CallExpression: (node) => {
|
|
17
|
+
if (!isCallOf(node, "Effect", "sync")) return Effect.void;
|
|
18
|
+
if (!("arguments" in node) || !Array.isArray(node.arguments) || node.arguments.length < 1) return Effect.void;
|
|
19
|
+
const arg = node.arguments[0];
|
|
20
|
+
if (arg == null || typeof arg !== "object" || !("type" in arg)) return Effect.void;
|
|
21
|
+
if (arg.type === "ArrowFunctionExpression" && "body" in arg) {
|
|
22
|
+
const body = arg.body;
|
|
23
|
+
if (body != null && typeof body === "object" && "type" in body && body.type === "CallExpression") {
|
|
24
|
+
const callee = body["callee"];
|
|
25
|
+
if (callee != null && typeof callee === "object" && "type" in callee && callee.type === "MemberExpression" && "object" in callee && callee.object != null && typeof callee.object === "object" && "name" in callee.object && callee.object.name === "console") return Effect.void;
|
|
26
|
+
return ctx.report(make({
|
|
27
|
+
node,
|
|
28
|
+
message: "Effect.sync wrapping a plain function call. Consider if Effect.sync is necessary."
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return Effect.void;
|
|
33
|
+
} };
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
//#endregion
|
|
37
|
+
export { noEffectSyncWrapper };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
2
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
3
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
4
|
+
import "../vendor/effect-oxlint/index.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
//#region src/rules/no-effect-type-alias.ts
|
|
7
|
+
/**
|
|
8
|
+
* Ban `type X = Effect.Effect<...>` type aliases.
|
|
9
|
+
*
|
|
10
|
+
* Keep Effect types on service methods or inline at call site.
|
|
11
|
+
*
|
|
12
|
+
* Source: biome-effect-linting-rules/no-effect-type-alias
|
|
13
|
+
*/
|
|
14
|
+
const containsEffectType = (node) => {
|
|
15
|
+
if (node == null || typeof node !== "object") return false;
|
|
16
|
+
const n = node;
|
|
17
|
+
if (n["type"] === "TSTypeReference" && "typeName" in n && n["typeName"] != null && typeof n["typeName"] === "object") {
|
|
18
|
+
const typeName = n["typeName"];
|
|
19
|
+
if (typeName["type"] === "TSQualifiedName" && "left" in typeName && "right" in typeName) {
|
|
20
|
+
const left = typeName["left"];
|
|
21
|
+
const right = typeName["right"];
|
|
22
|
+
if (left != null && left["type"] === "Identifier" && left["name"] === "Effect" && right != null && right["type"] === "Identifier" && right["name"] === "Effect") return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if ("typeAnnotation" in n && containsEffectType(n["typeAnnotation"])) return true;
|
|
26
|
+
if ("typeParameters" in n && n["typeParameters"] != null && typeof n["typeParameters"] === "object") {
|
|
27
|
+
const tp = n["typeParameters"];
|
|
28
|
+
if ("params" in tp && Array.isArray(tp["params"])) {
|
|
29
|
+
for (const p of tp["params"]) if (containsEffectType(p)) return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
};
|
|
34
|
+
const noEffectTypeAlias = define({
|
|
35
|
+
name: "no-effect-type-alias",
|
|
36
|
+
meta: meta({
|
|
37
|
+
type: "suggestion",
|
|
38
|
+
description: "Avoid Effect type aliases. Keep Effect types on service methods or inline."
|
|
39
|
+
}),
|
|
40
|
+
create: function* () {
|
|
41
|
+
const ctx = yield* RuleContext;
|
|
42
|
+
return { TSTypeAliasDeclaration: (node) => {
|
|
43
|
+
if ("typeAnnotation" in node && containsEffectType(node.typeAnnotation)) return ctx.report(make({
|
|
44
|
+
node,
|
|
45
|
+
message: "Avoid type alias wrapping Effect.Effect. Let Effect types flow from service methods."
|
|
46
|
+
}));
|
|
47
|
+
return Effect.void;
|
|
48
|
+
} };
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { noEffectTypeAlias };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
2
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
3
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
4
|
+
import "../vendor/effect-oxlint/index.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
//#region src/rules/no-effect-wrapper-alias.ts
|
|
7
|
+
/**
|
|
8
|
+
* Ban wrapper functions that just alias an Effect.fn or pipe(Effect.fn(...)).
|
|
9
|
+
*
|
|
10
|
+
* Inline the pipeline at the call site or define a real domain function.
|
|
11
|
+
*
|
|
12
|
+
* Source: biome-effect-linting-rules/no-effect-wrapper-alias
|
|
13
|
+
*/
|
|
14
|
+
const isEffectFnCall = (node) => {
|
|
15
|
+
if (node == null || typeof node !== "object") return false;
|
|
16
|
+
const n = node;
|
|
17
|
+
if (n["type"] !== "CallExpression") return false;
|
|
18
|
+
const callee = n["callee"];
|
|
19
|
+
if (callee == null || typeof callee !== "object") return false;
|
|
20
|
+
const c = callee;
|
|
21
|
+
if (c["type"] !== "MemberExpression") return false;
|
|
22
|
+
const obj = c["object"];
|
|
23
|
+
const prop = c["property"];
|
|
24
|
+
return obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" && prop != null && prop["type"] === "Identifier" && prop["name"] === "fn";
|
|
25
|
+
};
|
|
26
|
+
const isPipeWrappingEffectFn = (node) => {
|
|
27
|
+
if (node == null || typeof node !== "object") return false;
|
|
28
|
+
const n = node;
|
|
29
|
+
if (n["type"] !== "CallExpression") return false;
|
|
30
|
+
const callee = n["callee"];
|
|
31
|
+
if (callee == null || typeof callee !== "object") return false;
|
|
32
|
+
const c = callee;
|
|
33
|
+
if (!(c["type"] === "Identifier" && c["name"] === "pipe")) return false;
|
|
34
|
+
const args = n["arguments"];
|
|
35
|
+
if (!Array.isArray(args) || args.length === 0) return false;
|
|
36
|
+
return isEffectFnCall(args[0]);
|
|
37
|
+
};
|
|
38
|
+
const noEffectWrapperAlias = define({
|
|
39
|
+
name: "no-effect-wrapper-alias",
|
|
40
|
+
meta: meta({
|
|
41
|
+
type: "suggestion",
|
|
42
|
+
description: "Avoid wrapper functions aliasing Effect.fn. Inline or define a real domain function."
|
|
43
|
+
}),
|
|
44
|
+
create: function* () {
|
|
45
|
+
const ctx = yield* RuleContext;
|
|
46
|
+
return { VariableDeclarator: (node) => {
|
|
47
|
+
if (!("init" in node) || node.init == null) return Effect.void;
|
|
48
|
+
const init = node.init;
|
|
49
|
+
if (isEffectFnCall(init)) return ctx.report(make({
|
|
50
|
+
node,
|
|
51
|
+
message: "Avoid aliasing Effect.fn. Inline the pipeline or define a real domain function."
|
|
52
|
+
}));
|
|
53
|
+
if (isPipeWrappingEffectFn(init)) return ctx.report(make({
|
|
54
|
+
node,
|
|
55
|
+
message: "Avoid wrapping Effect.fn in pipe. Inline the pipeline or define a real domain function."
|
|
56
|
+
}));
|
|
57
|
+
if (typeof init === "object" && "type" in init && (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") && "body" in init) {
|
|
58
|
+
const body = init.body;
|
|
59
|
+
if (isEffectFnCall(body) || isPipeWrappingEffectFn(body)) return ctx.report(make({
|
|
60
|
+
node,
|
|
61
|
+
message: "Avoid wrapper function aliasing Effect.fn. Inline or define a real domain function."
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
return Effect.void;
|
|
65
|
+
} };
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
//#endregion
|
|
69
|
+
export { noEffectWrapperAlias };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
2
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
3
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
4
|
+
import "../vendor/effect-oxlint/index.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
//#region src/rules/no-extends-native-error.ts
|
|
7
|
+
/**
|
|
8
|
+
* Ban `class Foo extends Error`.
|
|
9
|
+
*
|
|
10
|
+
* Use Schema.TaggedErrorClass or Data.TaggedError instead.
|
|
11
|
+
*
|
|
12
|
+
* Source: language-service/extendsNativeError
|
|
13
|
+
*/
|
|
14
|
+
const nativeErrors = new Set([
|
|
15
|
+
"Error",
|
|
16
|
+
"TypeError",
|
|
17
|
+
"RangeError",
|
|
18
|
+
"ReferenceError",
|
|
19
|
+
"SyntaxError",
|
|
20
|
+
"URIError",
|
|
21
|
+
"EvalError"
|
|
22
|
+
]);
|
|
23
|
+
const noExtendsNativeError = define({
|
|
24
|
+
name: "no-extends-native-error",
|
|
25
|
+
meta: meta({
|
|
26
|
+
type: "suggestion",
|
|
27
|
+
description: "Avoid extending native Error. Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
28
|
+
}),
|
|
29
|
+
create: function* () {
|
|
30
|
+
const ctx = yield* RuleContext;
|
|
31
|
+
return { ClassDeclaration: (node) => {
|
|
32
|
+
if ("superClass" in node && node.superClass != null && typeof node.superClass === "object" && "type" in node.superClass && node.superClass.type === "Identifier" && "name" in node.superClass && typeof node.superClass.name === "string" && nativeErrors.has(node.superClass.name)) return ctx.report(make({
|
|
33
|
+
node,
|
|
34
|
+
message: "Avoid extending native Error. Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
35
|
+
}));
|
|
36
|
+
return Effect.void;
|
|
37
|
+
} };
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
//#endregion
|
|
41
|
+
export { noExtendsNativeError };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isCallOf } from "../vendor/effect-oxlint/AST.js";
|
|
2
|
+
import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
3
|
+
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
4
|
+
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
5
|
+
import "../vendor/effect-oxlint/index.js";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
//#region src/rules/no-flatmap-ladder.ts
|
|
8
|
+
const isEffectFlatMap = (node) => isCallOf(node, "Effect", "flatMap");
|
|
9
|
+
const containsEffectFlatMap = (node) => {
|
|
10
|
+
if (node == null || typeof node !== "object") return false;
|
|
11
|
+
const n = node;
|
|
12
|
+
if (n["type"] === "CallExpression" && "callee" in n && "arguments" in n) {
|
|
13
|
+
if (isEffectFlatMap(node)) return true;
|
|
14
|
+
}
|
|
15
|
+
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
16
|
+
for (const arg of n["arguments"]) if (containsEffectFlatMap(arg)) return true;
|
|
17
|
+
}
|
|
18
|
+
if ("body" in n) return containsEffectFlatMap(n["body"]);
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
const noFlatmapLadder = define({
|
|
22
|
+
name: "no-flatmap-ladder",
|
|
23
|
+
meta: meta({
|
|
24
|
+
type: "suggestion",
|
|
25
|
+
description: "Avoid nested Effect.flatMap (flatMap ladder). Build context once with Effect.all and run a single flatMap."
|
|
26
|
+
}),
|
|
27
|
+
create: function* () {
|
|
28
|
+
const ctx = yield* RuleContext;
|
|
29
|
+
return { CallExpression: (node) => {
|
|
30
|
+
const ce = node;
|
|
31
|
+
if (!isEffectFlatMap(ce)) return Effect.void;
|
|
32
|
+
if (!("arguments" in ce) || !Array.isArray(ce.arguments)) return Effect.void;
|
|
33
|
+
for (const arg of ce.arguments) if (arg != null && typeof arg === "object" && "type" in arg) {
|
|
34
|
+
if (arg.type === "CallExpression" && isEffectFlatMap(arg)) return ctx.report(make({
|
|
35
|
+
node,
|
|
36
|
+
message: "Nested Effect.flatMap detected. Build context with Effect.all, then single flatMap."
|
|
37
|
+
}));
|
|
38
|
+
if ((arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") && "body" in arg && containsEffectFlatMap(arg.body)) return ctx.report(make({
|
|
39
|
+
node,
|
|
40
|
+
message: "Nested Effect.flatMap detected. Build context with Effect.all, then single flatMap."
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
return Effect.void;
|
|
44
|
+
} };
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
//#endregion
|
|
48
|
+
export { noFlatmapLadder };
|