oxlint-plugin-effect 0.2.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -29
- package/dist/presets/index.d.ts +3 -9
- package/dist/presets/index.js +3 -17
- package/dist/presets/recommended.d.ts +17 -0
- package/dist/presets/recommended.js +17 -0
- package/dist/rules/index.d.ts +4 -59
- package/dist/rules/index.js +4 -114
- package/dist/rules/no-async-function.js +8 -13
- package/dist/rules/no-dynamic-imports.js +89 -60
- package/dist/rules/no-globals.js +164 -127
- package/dist/rules/no-new-error.js +26 -13
- package/dist/rules/no-new-promise.js +22 -21
- package/dist/rules/no-node-builtin-import.js +98 -41
- package/dist/rules/no-throw-statement.js +4 -16
- package/dist/rules/no-try-catch.js +5 -17
- package/package.json +2 -2
- package/dist/presets/core.d.ts +0 -49
- package/dist/presets/core.js +0 -49
- package/dist/presets/effect-native.d.ts +0 -28
- package/dist/presets/effect-native.js +0 -28
- package/dist/presets/full.d.ts +0 -67
- package/dist/presets/full.js +0 -67
- package/dist/presets/functional.d.ts +0 -22
- package/dist/presets/functional.js +0 -22
- package/dist/presets/strict.d.ts +0 -71
- package/dist/presets/strict.js +0 -71
- package/dist/presets/v3-strict.d.ts +0 -16
- package/dist/presets/v3-strict.js +0 -16
- package/dist/presets/v3.d.ts +0 -18
- package/dist/presets/v3.js +0 -18
- package/dist/rules/_effect-context.d.ts +0 -19
- package/dist/rules/_effect-context.js +0 -22
- package/dist/rules/no-arrow-ladder.d.ts +0 -6
- package/dist/rules/no-arrow-ladder.js +0 -61
- package/dist/rules/no-catch-all-to-map-error.d.ts +0 -6
- package/dist/rules/no-catch-all-to-map-error.js +0 -67
- package/dist/rules/no-effect-as.d.ts +0 -6
- package/dist/rules/no-effect-as.js +0 -16
- package/dist/rules/no-effect-async.d.ts +0 -6
- package/dist/rules/no-effect-async.js +0 -16
- package/dist/rules/no-effect-fn-generator.d.ts +0 -6
- package/dist/rules/no-effect-fn-generator.js +0 -30
- package/dist/rules/no-effect-gen-adapter.d.ts +0 -6
- package/dist/rules/no-effect-gen-adapter.js +0 -30
- package/dist/rules/no-effect-map-flatten.d.ts +0 -6
- package/dist/rules/no-effect-map-flatten.js +0 -53
- package/dist/rules/no-effect-map-void.d.ts +0 -6
- package/dist/rules/no-effect-map-void.js +0 -40
- package/dist/rules/no-effect-never.d.ts +0 -6
- package/dist/rules/no-effect-never.js +0 -16
- package/dist/rules/no-effect-orElse-ladder.d.ts +0 -6
- package/dist/rules/no-effect-orElse-ladder.js +0 -53
- package/dist/rules/no-effect-succeed-string.d.ts +0 -6
- package/dist/rules/no-effect-succeed-string.js +0 -30
- package/dist/rules/no-effect-succeed-variable.d.ts +0 -6
- package/dist/rules/no-effect-succeed-variable.js +0 -30
- package/dist/rules/no-effect-succeed-void.d.ts +0 -6
- package/dist/rules/no-effect-succeed-void.js +0 -33
- package/dist/rules/no-effect-sync-console.d.ts +0 -6
- package/dist/rules/no-effect-sync-console.js +0 -63
- package/dist/rules/no-effect-sync-wrapper.d.ts +0 -6
- package/dist/rules/no-effect-sync-wrapper.js +0 -37
- package/dist/rules/no-effect-type-alias.d.ts +0 -6
- package/dist/rules/no-effect-type-alias.js +0 -52
- package/dist/rules/no-effect-wrapper-alias.d.ts +0 -6
- package/dist/rules/no-effect-wrapper-alias.js +0 -69
- package/dist/rules/no-extends-native-error.d.ts +0 -6
- package/dist/rules/no-extends-native-error.js +0 -41
- package/dist/rules/no-flatmap-ladder.d.ts +0 -6
- package/dist/rules/no-flatmap-ladder.js +0 -48
- package/dist/rules/no-fromnullable-coalesce.d.ts +0 -6
- package/dist/rules/no-fromnullable-coalesce.js +0 -36
- package/dist/rules/no-global-error-in-catch.d.ts +0 -6
- package/dist/rules/no-global-error-in-catch.js +0 -75
- package/dist/rules/no-global-error-in-failure.d.ts +0 -6
- package/dist/rules/no-global-error-in-failure.js +0 -54
- package/dist/rules/no-hand-rolled-tagged-union.d.ts +0 -6
- package/dist/rules/no-hand-rolled-tagged-union.js +0 -70
- package/dist/rules/no-if-statement.d.ts +0 -6
- package/dist/rules/no-if-statement.js +0 -18
- package/dist/rules/no-iife-wrapper.d.ts +0 -6
- package/dist/rules/no-iife-wrapper.js +0 -37
- package/dist/rules/no-inline-provide.d.ts +0 -6
- package/dist/rules/no-inline-provide.js +0 -31
- package/dist/rules/no-instanceof-schema.d.ts +0 -6
- package/dist/rules/no-instanceof-schema.js +0 -35
- package/dist/rules/no-make-unsafe.d.ts +0 -6
- package/dist/rules/no-make-unsafe.js +0 -30
- package/dist/rules/no-manual-effect-channels.d.ts +0 -6
- package/dist/rules/no-manual-effect-channels.js +0 -40
- package/dist/rules/no-match-effect-branch.d.ts +0 -6
- package/dist/rules/no-match-effect-branch.js +0 -64
- package/dist/rules/no-match-void-branch.d.ts +0 -6
- package/dist/rules/no-match-void-branch.js +0 -50
- package/dist/rules/no-multiple-effect-provide.d.ts +0 -6
- package/dist/rules/no-multiple-effect-provide.js +0 -45
- package/dist/rules/no-nested-effect-call.d.ts +0 -6
- package/dist/rules/no-nested-effect-call.js +0 -97
- package/dist/rules/no-nested-effect-gen.d.ts +0 -6
- package/dist/rules/no-nested-effect-gen.js +0 -46
- package/dist/rules/no-nested-pipe.d.ts +0 -6
- package/dist/rules/no-nested-pipe.js +0 -38
- package/dist/rules/no-option-as.d.ts +0 -6
- package/dist/rules/no-option-as.js +0 -16
- package/dist/rules/no-option-boolean-normalization.d.ts +0 -6
- package/dist/rules/no-option-boolean-normalization.js +0 -50
- package/dist/rules/no-positional-log-error.d.ts +0 -6
- package/dist/rules/no-positional-log-error.js +0 -52
- package/dist/rules/no-promise-control-flow-in-tests.d.ts +0 -6
- package/dist/rules/no-promise-control-flow-in-tests.js +0 -94
- package/dist/rules/no-redundant-schema-tag-identifier.d.ts +0 -6
- package/dist/rules/no-redundant-schema-tag-identifier.js +0 -46
- package/dist/rules/no-return-in-arrow.d.ts +0 -6
- package/dist/rules/no-return-in-arrow.js +0 -41
- package/dist/rules/no-return-nullish.d.ts +0 -6
- package/dist/rules/no-return-nullish.js +0 -47
- package/dist/rules/no-run-in-effect-gen.d.ts +0 -6
- package/dist/rules/no-run-in-effect-gen.js +0 -48
- package/dist/rules/no-runtime-run-fork.d.ts +0 -6
- package/dist/rules/no-runtime-run-fork.js +0 -19
- package/dist/rules/no-schema-struct-with-tag.d.ts +0 -6
- package/dist/rules/no-schema-struct-with-tag.js +0 -48
- package/dist/rules/no-schema-struct.d.ts +0 -6
- package/dist/rules/no-schema-struct.js +0 -28
- package/dist/rules/no-schema-union-of-literals.d.ts +0 -6
- package/dist/rules/no-schema-union-of-literals.js +0 -35
- package/dist/rules/no-sleep-in-tests.d.ts +0 -6
- package/dist/rules/no-sleep-in-tests.js +0 -55
- package/dist/rules/no-spread.d.ts +0 -6
- package/dist/rules/no-spread.js +0 -26
- package/dist/rules/no-string-sentinel-const.d.ts +0 -6
- package/dist/rules/no-string-sentinel-const.js +0 -38
- package/dist/rules/no-switch-statement.d.ts +0 -6
- package/dist/rules/no-switch-statement.js +0 -16
- package/dist/rules/no-unnecessary-arrow-block.d.ts +0 -6
- package/dist/rules/no-unnecessary-arrow-block.js +0 -34
- package/dist/rules/no-unnecessary-effect-gen.d.ts +0 -6
- package/dist/rules/no-unnecessary-effect-gen.js +0 -35
- package/dist/rules/no-unnecessary-pipe-chain.d.ts +0 -6
- package/dist/rules/no-unnecessary-pipe-chain.js +0 -36
- package/dist/rules/no-unnecessary-pipe.d.ts +0 -6
- package/dist/rules/no-unnecessary-pipe.js +0 -29
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { memberNames } 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 Option from "effect/Option";
|
|
7
|
-
import * as Effect from "effect/Effect";
|
|
8
|
-
//#region src/rules/no-global-error-in-catch.ts
|
|
9
|
-
const nativeErrors = new Set([
|
|
10
|
-
"Error",
|
|
11
|
-
"TypeError",
|
|
12
|
-
"RangeError",
|
|
13
|
-
"ReferenceError",
|
|
14
|
-
"SyntaxError",
|
|
15
|
-
"URIError",
|
|
16
|
-
"EvalError"
|
|
17
|
-
]);
|
|
18
|
-
const catchMethods = new Set([
|
|
19
|
-
"catch",
|
|
20
|
-
"catchAll",
|
|
21
|
-
"catchAllCause",
|
|
22
|
-
"catchIf",
|
|
23
|
-
"catchCauseIf",
|
|
24
|
-
"catchTag",
|
|
25
|
-
"catchTags",
|
|
26
|
-
"catchDefect"
|
|
27
|
-
]);
|
|
28
|
-
const isNativeErrorNew = (node) => {
|
|
29
|
-
if (node.type !== "NewExpression") return false;
|
|
30
|
-
const callee = node["callee"];
|
|
31
|
-
return callee.type === "Identifier" && "name" in callee && nativeErrors.has(callee.name);
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Check if a function body returns `new Error(...)`.
|
|
35
|
-
*/
|
|
36
|
-
const bodyReturnsNativeError = (fn) => {
|
|
37
|
-
const body = fn["body"];
|
|
38
|
-
if (body.type !== "BlockStatement") return isNativeErrorNew(body);
|
|
39
|
-
const stmts = body["body"];
|
|
40
|
-
if (!Array.isArray(stmts) || stmts.length !== 1) return false;
|
|
41
|
-
const stmt = stmts[0];
|
|
42
|
-
if (stmt.type !== "ReturnStatement") return false;
|
|
43
|
-
const arg = stmt["argument"];
|
|
44
|
-
return arg != null && typeof arg === "object" && isNativeErrorNew(arg);
|
|
45
|
-
};
|
|
46
|
-
const noGlobalErrorInCatch = define({
|
|
47
|
-
name: "no-global-error-in-catch",
|
|
48
|
-
meta: meta({
|
|
49
|
-
type: "suggestion",
|
|
50
|
-
description: "Avoid native Error in Effect catch handlers. Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
51
|
-
}),
|
|
52
|
-
create: function* () {
|
|
53
|
-
const ctx = yield* RuleContext;
|
|
54
|
-
return { CallExpression: (node) => {
|
|
55
|
-
const call = node;
|
|
56
|
-
const names = Option.getOrUndefined(memberNames(call.callee));
|
|
57
|
-
if (names === void 0) return Effect.void;
|
|
58
|
-
const [obj, prop] = names;
|
|
59
|
-
if (obj !== "Effect") return Effect.void;
|
|
60
|
-
if (!catchMethods.has(prop)) return Effect.void;
|
|
61
|
-
const args = call.arguments;
|
|
62
|
-
if (args.length === 0) return Effect.void;
|
|
63
|
-
const callback = args[args.length - 1];
|
|
64
|
-
if (callback === void 0) return Effect.void;
|
|
65
|
-
if (callback.type !== "ArrowFunctionExpression" && callback.type !== "FunctionExpression") return Effect.void;
|
|
66
|
-
if (bodyReturnsNativeError(callback)) return ctx.report(make({
|
|
67
|
-
node: callback,
|
|
68
|
-
message: "Avoid native Error in Effect catch handler. Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
69
|
-
}));
|
|
70
|
-
return Effect.void;
|
|
71
|
-
} };
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
//#endregion
|
|
75
|
-
export { noGlobalErrorInCatch };
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { matchCallOf } 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 Option from "effect/Option";
|
|
7
|
-
import * as Effect from "effect/Effect";
|
|
8
|
-
//#region src/rules/no-global-error-in-failure.ts
|
|
9
|
-
const nativeErrors = new Set([
|
|
10
|
-
"Error",
|
|
11
|
-
"TypeError",
|
|
12
|
-
"RangeError",
|
|
13
|
-
"ReferenceError",
|
|
14
|
-
"SyntaxError",
|
|
15
|
-
"URIError",
|
|
16
|
-
"EvalError"
|
|
17
|
-
]);
|
|
18
|
-
const isNativeErrorNew = (node) => {
|
|
19
|
-
if (node.type !== "NewExpression") return false;
|
|
20
|
-
const callee = node["callee"];
|
|
21
|
-
return callee.type === "Identifier" && "name" in callee && nativeErrors.has(callee.name);
|
|
22
|
-
};
|
|
23
|
-
const noGlobalErrorInFailure = define({
|
|
24
|
-
name: "no-global-error-in-failure",
|
|
25
|
-
meta: meta({
|
|
26
|
-
type: "suggestion",
|
|
27
|
-
description: "Avoid native Error in Effect.fail(). Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
28
|
-
}),
|
|
29
|
-
create: function* () {
|
|
30
|
-
const ctx = yield* RuleContext;
|
|
31
|
-
return { CallExpression: (node) => {
|
|
32
|
-
const call = node;
|
|
33
|
-
if (Option.isNone(matchCallOf(call, "Effect", "fail")) && Option.isNone(matchCallOf(call, "Effect", "failSync")) && Option.isNone(matchCallOf(call, "Effect", "failCause"))) return Effect.void;
|
|
34
|
-
const args = call.arguments;
|
|
35
|
-
if (args.length === 0) return Effect.void;
|
|
36
|
-
const arg = args[args.length - 1];
|
|
37
|
-
if (arg === void 0) return Effect.void;
|
|
38
|
-
if (isNativeErrorNew(arg)) return ctx.report(make({
|
|
39
|
-
node: arg,
|
|
40
|
-
message: "Avoid native Error in Effect.fail(). Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
41
|
-
}));
|
|
42
|
-
if (arg.type === "ArrowFunctionExpression") {
|
|
43
|
-
const body = arg["body"];
|
|
44
|
-
if (body.type !== "BlockStatement" && isNativeErrorNew(body)) return ctx.report(make({
|
|
45
|
-
node: arg,
|
|
46
|
-
message: "Avoid native Error in Effect.failSync(). Use Schema.TaggedErrorClass or Data.TaggedError."
|
|
47
|
-
}));
|
|
48
|
-
}
|
|
49
|
-
return Effect.void;
|
|
50
|
-
} };
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
//#endregion
|
|
54
|
-
export { noGlobalErrorInFailure };
|
|
@@ -1,70 +0,0 @@
|
|
|
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-hand-rolled-tagged-union.ts
|
|
7
|
-
const isAstNode = (value) => typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
8
|
-
const getNodeField = (node, field) => {
|
|
9
|
-
const value = node[field];
|
|
10
|
-
return isAstNode(value) ? value : void 0;
|
|
11
|
-
};
|
|
12
|
-
const getStringField = (node, field) => {
|
|
13
|
-
const value = node[field];
|
|
14
|
-
return typeof value === "string" ? value : void 0;
|
|
15
|
-
};
|
|
16
|
-
const getNodeArrayField = (node, field) => {
|
|
17
|
-
const value = node[field];
|
|
18
|
-
return Array.isArray(value) ? value.filter(isAstNode) : void 0;
|
|
19
|
-
};
|
|
20
|
-
const getPropertyKeyName = (node) => {
|
|
21
|
-
if (node.type === "Identifier" || node.type === "StringLiteral" || node.type === "Literal") return getStringField(node, "name") ?? getStringField(node, "value");
|
|
22
|
-
};
|
|
23
|
-
const isPascalCaseTag = (value) => {
|
|
24
|
-
if (value.length === 0) return false;
|
|
25
|
-
const first = value.charAt(0);
|
|
26
|
-
return first === first.toUpperCase() && first !== first.toLowerCase();
|
|
27
|
-
};
|
|
28
|
-
const isReportableTagLiteral = (member) => {
|
|
29
|
-
if (member.type !== "TSPropertySignature") return false;
|
|
30
|
-
const key = getNodeField(member, "key");
|
|
31
|
-
if (key === void 0 || getPropertyKeyName(key) !== "_tag") return false;
|
|
32
|
-
const annotation = getNodeField(member, "typeAnnotation");
|
|
33
|
-
const inner = annotation === void 0 ? void 0 : getNodeField(annotation, "typeAnnotation");
|
|
34
|
-
if (inner?.type !== "TSLiteralType") return false;
|
|
35
|
-
const literal = getNodeField(inner, "literal");
|
|
36
|
-
if (literal === void 0) return false;
|
|
37
|
-
if (literal.type !== "StringLiteral" && literal.type !== "Literal") return false;
|
|
38
|
-
const value = getStringField(literal, "value");
|
|
39
|
-
return value !== void 0 && isPascalCaseTag(value);
|
|
40
|
-
};
|
|
41
|
-
const literalHasTag = (node) => {
|
|
42
|
-
if (node.type !== "TSTypeLiteral") return false;
|
|
43
|
-
const members = getNodeArrayField(node, "members");
|
|
44
|
-
return members !== void 0 && members.some(isReportableTagLiteral);
|
|
45
|
-
};
|
|
46
|
-
const noHandRolledTaggedUnion = define({
|
|
47
|
-
name: "no-hand-rolled-tagged-union",
|
|
48
|
-
meta: meta({
|
|
49
|
-
type: "suggestion",
|
|
50
|
-
description: "Use Schema.TaggedUnion, Schema.TaggedStruct, or Schema.TaggedErrorClass instead of inline _tag union literals."
|
|
51
|
-
}),
|
|
52
|
-
create: function* () {
|
|
53
|
-
const ctx = yield* RuleContext;
|
|
54
|
-
return { TSUnionType: (node) => {
|
|
55
|
-
const types = getNodeArrayField(node, "types");
|
|
56
|
-
if (types === void 0 || types.length < 2) return Effect.void;
|
|
57
|
-
let tagged = 0;
|
|
58
|
-
for (const type of types) {
|
|
59
|
-
if (literalHasTag(type)) tagged += 1;
|
|
60
|
-
if (tagged >= 2) return ctx.report(make({
|
|
61
|
-
node,
|
|
62
|
-
message: "Hand-rolled `_tag` discriminated union. Use Schema.TaggedUnion, Schema.TaggedStruct, or Schema.TaggedErrorClass instead."
|
|
63
|
-
}));
|
|
64
|
-
}
|
|
65
|
-
return Effect.void;
|
|
66
|
-
} };
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
//#endregion
|
|
70
|
-
export { noHandRolledTaggedUnion };
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { banStatement } from "../vendor/effect-oxlint/Rule.js";
|
|
2
|
-
import "../vendor/effect-oxlint/index.js";
|
|
3
|
-
//#region src/rules/no-if-statement.ts
|
|
4
|
-
/**
|
|
5
|
-
* Ban `if` statements in Effect codebases.
|
|
6
|
-
*
|
|
7
|
-
* Use `Option.match`, `Either.match`, `Match.value`, or data combinators.
|
|
8
|
-
*
|
|
9
|
-
* Source: biome-effect-linting-rules/no-if-statement
|
|
10
|
-
*
|
|
11
|
-
* Note: Very opinionated — recommended for strict functional presets only.
|
|
12
|
-
*/
|
|
13
|
-
const noIfStatement = banStatement("IfStatement", {
|
|
14
|
-
message: "Avoid if statements. Use Option.match, Either.match, Match.value, or Effect.if.",
|
|
15
|
-
meta: { type: "suggestion" }
|
|
16
|
-
});
|
|
17
|
-
//#endregion
|
|
18
|
-
export { noIfStatement };
|
|
@@ -1,37 +0,0 @@
|
|
|
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-iife-wrapper.ts
|
|
7
|
-
/**
|
|
8
|
-
* Ban immediately-invoked function expressions (IIFEs).
|
|
9
|
-
*
|
|
10
|
-
* Use named const binding + flat pipeline.
|
|
11
|
-
*
|
|
12
|
-
* Source: biome-effect-linting-rules/no-iife-wrapper
|
|
13
|
-
*/
|
|
14
|
-
const noIifeWrapper = define({
|
|
15
|
-
name: "no-iife-wrapper",
|
|
16
|
-
meta: meta({
|
|
17
|
-
type: "suggestion",
|
|
18
|
-
description: "Avoid IIFEs. Use named const bindings + flat pipeline."
|
|
19
|
-
}),
|
|
20
|
-
create: function* () {
|
|
21
|
-
const ctx = yield* RuleContext;
|
|
22
|
-
return { CallExpression: (node) => {
|
|
23
|
-
if (!("callee" in node)) return Effect.void;
|
|
24
|
-
const callee = node.callee;
|
|
25
|
-
if (callee == null || typeof callee !== "object" || !("type" in callee)) return Effect.void;
|
|
26
|
-
const isWrappedFn = callee.type === "ArrowFunctionExpression" || callee.type === "FunctionExpression";
|
|
27
|
-
const isParenWrapped = callee.type === "ParenthesizedExpression" && "expression" in callee && callee.expression != null && typeof callee.expression === "object" && "type" in callee.expression && (callee.expression.type === "ArrowFunctionExpression" || callee.expression.type === "FunctionExpression");
|
|
28
|
-
if (isWrappedFn || isParenWrapped) return ctx.report(make({
|
|
29
|
-
node,
|
|
30
|
-
message: "Avoid IIFEs. Use named const binding + flat pipeline."
|
|
31
|
-
}));
|
|
32
|
-
return Effect.void;
|
|
33
|
-
} };
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
//#endregion
|
|
37
|
-
export { noIifeWrapper };
|
|
@@ -1,31 +0,0 @@
|
|
|
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 { merge } from "../vendor/effect-oxlint/Visitor.js";
|
|
5
|
-
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
6
|
-
import "../vendor/effect-oxlint/index.js";
|
|
7
|
-
import { makeEffectContextTracker } from "./_effect-context.js";
|
|
8
|
-
import * as Effect from "effect/Effect";
|
|
9
|
-
import * as Ref from "effect/Ref";
|
|
10
|
-
//#region src/rules/no-inline-provide.ts
|
|
11
|
-
const noInlineProvide = define({
|
|
12
|
-
name: "no-inline-provide",
|
|
13
|
-
meta: meta({
|
|
14
|
-
type: "suggestion",
|
|
15
|
-
description: "Avoid Effect.provide inside Effect.gen/fn. Provide layers at the boundary."
|
|
16
|
-
}),
|
|
17
|
-
create: function* () {
|
|
18
|
-
const ctx = yield* RuleContext;
|
|
19
|
-
const [depth, tracker] = yield* makeEffectContextTracker;
|
|
20
|
-
return merge(tracker, { CallExpression: (node) => Effect.flatMap(Ref.get(depth), (d) => {
|
|
21
|
-
if (d <= 0) return Effect.void;
|
|
22
|
-
if (!isCallOf(node, "Effect", "provide")) return Effect.void;
|
|
23
|
-
return ctx.report(make({
|
|
24
|
-
node,
|
|
25
|
-
message: "Avoid Effect.provide inside Effect.gen/fn. Provide layers at the boundary."
|
|
26
|
-
}));
|
|
27
|
-
}) });
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
//#endregion
|
|
31
|
-
export { noInlineProvide };
|
|
@@ -1,35 +0,0 @@
|
|
|
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-instanceof-schema.ts
|
|
7
|
-
/**
|
|
8
|
-
* Ban `instanceof` checks against Schema-derived classes.
|
|
9
|
-
*
|
|
10
|
-
* Use `Schema.is(SchemaType)` instead.
|
|
11
|
-
*
|
|
12
|
-
* Source: language-service/instanceOfSchema
|
|
13
|
-
*
|
|
14
|
-
* Note: Without type information, we detect common Effect Schema class names
|
|
15
|
-
* used in `instanceof` checks. This is a best-effort heuristic.
|
|
16
|
-
*/
|
|
17
|
-
const noInstanceofSchema = define({
|
|
18
|
-
name: "no-instanceof-schema",
|
|
19
|
-
meta: meta({
|
|
20
|
-
type: "suggestion",
|
|
21
|
-
description: "Avoid instanceof with Schema types. Use Schema.is(SchemaType) instead."
|
|
22
|
-
}),
|
|
23
|
-
create: function* () {
|
|
24
|
-
const ctx = yield* RuleContext;
|
|
25
|
-
return { BinaryExpression: (node) => {
|
|
26
|
-
if ("operator" in node && node.operator === "instanceof") return ctx.report(make({
|
|
27
|
-
node,
|
|
28
|
-
message: "Avoid instanceof. Add a static `is` field to your class: `static is = Schema.is(this)`, then use `MyClass.is(value)`. For tagged types, use `_tag` checks."
|
|
29
|
-
}));
|
|
30
|
-
return Effect.void;
|
|
31
|
-
} };
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
//#endregion
|
|
35
|
-
export { noInstanceofSchema };
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { memberNames } 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 Option from "effect/Option";
|
|
7
|
-
import * as Effect from "effect/Effect";
|
|
8
|
-
//#region src/rules/no-make-unsafe.ts
|
|
9
|
-
const noMakeUnsafe = define({
|
|
10
|
-
name: "no-make-unsafe",
|
|
11
|
-
meta: meta({
|
|
12
|
-
type: "problem",
|
|
13
|
-
description: "Avoid makeUnsafe constructors. Use safe Effectful or Option-returning construction."
|
|
14
|
-
}),
|
|
15
|
-
create: function* () {
|
|
16
|
-
const ctx = yield* RuleContext;
|
|
17
|
-
return { CallExpression: (node) => {
|
|
18
|
-
const call = node;
|
|
19
|
-
if (call.callee.type !== "MemberExpression") return Effect.void;
|
|
20
|
-
if (Option.isNone(memberNames(call.callee))) return Effect.void;
|
|
21
|
-
if (Option.getOrUndefined(memberNames(call.callee))?.[1] !== "makeUnsafe") return Effect.void;
|
|
22
|
-
return ctx.report(make({
|
|
23
|
-
node,
|
|
24
|
-
message: "Do not call `.makeUnsafe(...)`. Use safe Effectful/Option-returning construction instead of bypassing validation."
|
|
25
|
-
}));
|
|
26
|
-
} };
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
//#endregion
|
|
30
|
-
export { noMakeUnsafe };
|
|
@@ -1,40 +0,0 @@
|
|
|
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-manual-effect-channels.ts
|
|
7
|
-
/**
|
|
8
|
-
* Ban manual `Effect.Effect<A, E, R>` type annotations.
|
|
9
|
-
*
|
|
10
|
-
* Let return types infer from the Effect/Layer you return.
|
|
11
|
-
*
|
|
12
|
-
* Source: biome-effect-linting-rules/no-manual-effect-channels
|
|
13
|
-
*/
|
|
14
|
-
const noManualEffectChannels = define({
|
|
15
|
-
name: "no-manual-effect-channels",
|
|
16
|
-
meta: meta({
|
|
17
|
-
type: "suggestion",
|
|
18
|
-
description: "Avoid manual Effect.Effect<A, E, R> annotations. Let types infer from the returned Effect."
|
|
19
|
-
}),
|
|
20
|
-
create: function* () {
|
|
21
|
-
const ctx = yield* RuleContext;
|
|
22
|
-
return { TSTypeReference: (node) => {
|
|
23
|
-
if (!("typeName" in node) || node.typeName == null) return Effect.void;
|
|
24
|
-
const typeName = node.typeName;
|
|
25
|
-
if (typeName["type"] === "TSQualifiedName" && "left" in typeName && "right" in typeName) {
|
|
26
|
-
const left = typeName["left"];
|
|
27
|
-
const right = typeName["right"];
|
|
28
|
-
if (left != null && left["type"] === "Identifier" && (left["name"] === "Effect" || left["name"] === "Layer") && right != null && right["type"] === "Identifier" && (right["name"] === "Effect" || right["name"] === "Layer")) {
|
|
29
|
-
if ("typeParameters" in node && node.typeParameters != null && typeof node.typeParameters === "object" && "params" in node.typeParameters && Array.isArray(node.typeParameters["params"]) && node.typeParameters["params"].length > 0) return ctx.report(make({
|
|
30
|
-
node,
|
|
31
|
-
message: "Avoid manual Effect/Layer channel annotations. Let types infer from the returned value."
|
|
32
|
-
}));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return Effect.void;
|
|
36
|
-
} };
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
//#endregion
|
|
40
|
-
export { noManualEffectChannels };
|
|
@@ -1,64 +0,0 @@
|
|
|
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-match-effect-branch.ts
|
|
8
|
-
const sequencingMethods = new Set([
|
|
9
|
-
"flatMap",
|
|
10
|
-
"map",
|
|
11
|
-
"andThen",
|
|
12
|
-
"tap",
|
|
13
|
-
"zipRight",
|
|
14
|
-
"pipe"
|
|
15
|
-
]);
|
|
16
|
-
const containsEffectSequencing = (node) => {
|
|
17
|
-
if (node == null || typeof node !== "object") return false;
|
|
18
|
-
const n = node;
|
|
19
|
-
if (n["type"] === "CallExpression" && "callee" in n) {
|
|
20
|
-
const callee = n["callee"];
|
|
21
|
-
if (callee != null && callee["type"] === "MemberExpression") {
|
|
22
|
-
const obj = callee["object"];
|
|
23
|
-
const prop = callee["property"];
|
|
24
|
-
if (obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" && prop != null && prop["type"] === "Identifier" && typeof prop["name"] === "string" && sequencingMethods.has(prop["name"])) return true;
|
|
25
|
-
}
|
|
26
|
-
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
27
|
-
for (const arg of n["arguments"]) if (containsEffectSequencing(arg)) return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
if ("body" in n) {
|
|
31
|
-
if (Array.isArray(n["body"])) {
|
|
32
|
-
for (const child of n["body"]) if (containsEffectSequencing(child)) return true;
|
|
33
|
-
} else if (containsEffectSequencing(n["body"])) return true;
|
|
34
|
-
}
|
|
35
|
-
if ("argument" in n && containsEffectSequencing(n["argument"])) return true;
|
|
36
|
-
if ("expression" in n && containsEffectSequencing(n["expression"])) return true;
|
|
37
|
-
return false;
|
|
38
|
-
};
|
|
39
|
-
const noMatchEffectBranch = define({
|
|
40
|
-
name: "no-match-effect-branch",
|
|
41
|
-
meta: meta({
|
|
42
|
-
type: "suggestion",
|
|
43
|
-
description: "Avoid Effect sequencing inside Match branches. Select value in Match, run Effect outside."
|
|
44
|
-
}),
|
|
45
|
-
create: function* () {
|
|
46
|
-
const ctx = yield* RuleContext;
|
|
47
|
-
return { CallExpression: (node) => {
|
|
48
|
-
const ce = node;
|
|
49
|
-
const isMatchWhen = isCallOf(ce, "Match", "when");
|
|
50
|
-
const isMatchOrElse = isCallOf(ce, "Match", "orElse");
|
|
51
|
-
if (!isMatchWhen && !isMatchOrElse) return Effect.void;
|
|
52
|
-
if (!("arguments" in node) || !Array.isArray(node.arguments)) return Effect.void;
|
|
53
|
-
const callbackIdx = isMatchWhen ? 1 : 0;
|
|
54
|
-
const callback = node.arguments[callbackIdx];
|
|
55
|
-
if (callback != null && containsEffectSequencing(callback)) return ctx.report(make({
|
|
56
|
-
node,
|
|
57
|
-
message: "Avoid Effect sequencing inside Match branches. Select value in Match, then run one Effect pipeline outside."
|
|
58
|
-
}));
|
|
59
|
-
return Effect.void;
|
|
60
|
-
} };
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
//#endregion
|
|
64
|
-
export { noMatchEffectBranch };
|
|
@@ -1,50 +0,0 @@
|
|
|
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-match-void-branch.ts
|
|
8
|
-
const isEffectVoid = (node) => {
|
|
9
|
-
if (node == null || typeof node !== "object") return false;
|
|
10
|
-
const n = node;
|
|
11
|
-
if (n["type"] !== "MemberExpression") return false;
|
|
12
|
-
const obj = n["object"];
|
|
13
|
-
const prop = n["property"];
|
|
14
|
-
return obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" && prop != null && prop["type"] === "Identifier" && prop["name"] === "void";
|
|
15
|
-
};
|
|
16
|
-
const isVoidCallback = (node) => {
|
|
17
|
-
if (node == null || typeof node !== "object") return false;
|
|
18
|
-
const n = node;
|
|
19
|
-
if (n["type"] !== "ArrowFunctionExpression" && n["type"] !== "FunctionExpression") return false;
|
|
20
|
-
if (!("body" in n)) return false;
|
|
21
|
-
return isEffectVoid(n["body"]);
|
|
22
|
-
};
|
|
23
|
-
const noMatchVoidBranch = define({
|
|
24
|
-
name: "no-match-void-branch",
|
|
25
|
-
meta: meta({
|
|
26
|
-
type: "suggestion",
|
|
27
|
-
description: "Avoid no-op Match branches returning Effect.void. Remove the branch or restructure."
|
|
28
|
-
}),
|
|
29
|
-
create: function* () {
|
|
30
|
-
const ctx = yield* RuleContext;
|
|
31
|
-
return { CallExpression: (node) => {
|
|
32
|
-
const ce = node;
|
|
33
|
-
const isMatchWhen = isCallOf(ce, "Match", "when");
|
|
34
|
-
const isMatchOrElse = isCallOf(ce, "Match", "orElse");
|
|
35
|
-
if (!isMatchWhen && !isMatchOrElse) return Effect.void;
|
|
36
|
-
if (!("arguments" in node) || !Array.isArray(node.arguments)) return Effect.void;
|
|
37
|
-
if (isMatchOrElse && node.arguments.length >= 1 && isVoidCallback(node.arguments[0])) return ctx.report(make({
|
|
38
|
-
node,
|
|
39
|
-
message: "Match.orElse returning Effect.void is a no-op. Remove the branch or restructure."
|
|
40
|
-
}));
|
|
41
|
-
if (isMatchWhen && node.arguments.length >= 2 && isVoidCallback(node.arguments[1])) return ctx.report(make({
|
|
42
|
-
node,
|
|
43
|
-
message: "Match.when branch returning Effect.void is a no-op. Remove the branch or restructure."
|
|
44
|
-
}));
|
|
45
|
-
return Effect.void;
|
|
46
|
-
} };
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
//#endregion
|
|
50
|
-
export { noMatchVoidBranch };
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { calleeName, matchCallOf } 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 Option from "effect/Option";
|
|
7
|
-
import * as Effect from "effect/Effect";
|
|
8
|
-
//#region src/rules/no-multiple-effect-provide.ts
|
|
9
|
-
const isEffectProvide = (node) => {
|
|
10
|
-
if (node.type !== "CallExpression") return false;
|
|
11
|
-
return Option.isSome(matchCallOf(node, "Effect", "provide"));
|
|
12
|
-
};
|
|
13
|
-
const countProvides = (args) => {
|
|
14
|
-
let count = 0;
|
|
15
|
-
for (const arg of args) if (isEffectProvide(arg)) count++;
|
|
16
|
-
return count;
|
|
17
|
-
};
|
|
18
|
-
const noMultipleEffectProvide = define({
|
|
19
|
-
name: "no-multiple-effect-provide",
|
|
20
|
-
meta: meta({
|
|
21
|
-
type: "suggestion",
|
|
22
|
-
description: "Avoid consecutive Effect.provide() calls. Merge into Effect.provide(Layer.mergeAll(...))."
|
|
23
|
-
}),
|
|
24
|
-
create: function* () {
|
|
25
|
-
const ctx = yield* RuleContext;
|
|
26
|
-
return { CallExpression: (node) => {
|
|
27
|
-
const call = node;
|
|
28
|
-
const args = call.arguments;
|
|
29
|
-
if (args.length < 3) return Effect.void;
|
|
30
|
-
const calleeName$1 = Option.getOrUndefined(calleeName(call));
|
|
31
|
-
const callee = call.callee;
|
|
32
|
-
if (!(calleeName$1 === "pipe" || callee.type === "MemberExpression" && "property" in callee && callee["computed"] !== true && (() => {
|
|
33
|
-
const prop = callee["property"];
|
|
34
|
-
return prop != null && typeof prop === "object" && "name" in prop && prop.name === "pipe";
|
|
35
|
-
})())) return Effect.void;
|
|
36
|
-
if (countProvides(args) >= 2) return ctx.report(make({
|
|
37
|
-
node,
|
|
38
|
-
message: "Multiple Effect.provide() in pipe. Merge into Effect.provide(Layer.mergeAll(...))."
|
|
39
|
-
}));
|
|
40
|
-
return Effect.void;
|
|
41
|
-
} };
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
//#endregion
|
|
45
|
-
export { noMultipleEffectProvide };
|