oxlint-plugin-effect 0.16.0 → 0.16.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/README.md
CHANGED
|
@@ -67,8 +67,8 @@ Every recommended rule has `error` severity. The complexity rules carry their li
|
|
|
67
67
|
| `effect/noEffectBind` | Bans Effect.bind |
|
|
68
68
|
| `effect/preferCatchTag` | Replaces manual `_tag` predicates and `catchAll` dispatch with tagged recovery |
|
|
69
69
|
| `effect/preferEffectFn` | Requires `Effect.fn` for a generator operation that adds a span |
|
|
70
|
-
| `effect/preferMatchTagsExhaustive` | Requires exhaustive `Match` for return-only `_tag` switches and if chains
|
|
71
|
-
| `effect/preferPredicateIsTagged` | Replaces combined `_tag` comparisons with a named `Predicate` refinement
|
|
70
|
+
| `effect/preferMatchTagsExhaustive` | Requires exhaustive `Match` for return-only `_tag` switches and if chains in Effect files |
|
|
71
|
+
| `effect/preferPredicateIsTagged` | Replaces combined `_tag` comparisons with a named `Predicate` refinement in Effect files |
|
|
72
72
|
| `effect/preferServiceOf` | Checks inline Layer implementations through `Service.of` |
|
|
73
73
|
| `effect/requireNamedEffectFn` | Requires stable names for `Effect.fn` operations |
|
|
74
74
|
| `effect/requireSuppressionReason` | Requires lint, Effect, and TS suppressions to name their target and give a `--` reason |
|
|
@@ -21,5 +21,10 @@ const visibleNamespaces = (ctx, node, namespaces) => new Set([...namespaces].fil
|
|
|
21
21
|
})));
|
|
22
22
|
const isStaticMember = (node, namespaces, property) => node.type === "MemberExpression" && !node.computed && node.object.type === "Identifier" && namespaces.has(node.object.name) && node.property.type === "Identifier" && node.property.name === property;
|
|
23
23
|
const isStaticCall = (node, namespaces, property) => node.type === "CallExpression" && node.callee.type !== "Super" && isStaticMember(node.callee, namespaces, property);
|
|
24
|
+
/** Whether an import loads the effect package, one of its subpaths, or an @effect package. */
|
|
25
|
+
const isEffectPackageImport = (node) => {
|
|
26
|
+
const source = node.source.value;
|
|
27
|
+
return source === "effect" || source.startsWith("effect/") || source.startsWith("@effect/");
|
|
28
|
+
};
|
|
24
29
|
//#endregion
|
|
25
|
-
export { importedNamespaces, isStaticCall, isStaticMember, visibleNamespaces };
|
|
30
|
+
export { importedNamespaces, isEffectPackageImport, isStaticCall, isStaticMember, visibleNamespaces };
|
|
@@ -2,6 +2,7 @@ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
|
2
2
|
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
3
3
|
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
4
4
|
import "../vendor/effect-oxlint/index.js";
|
|
5
|
+
import { isEffectPackageImport } from "./_effect-namespaces.js";
|
|
5
6
|
import { hasOneTaggedSubject, isInsideCatchAllHandler, tagComparison, taggedSwitchSubject } from "./_tagged-control-flow.js";
|
|
6
7
|
import * as Arr from "effect/Array";
|
|
7
8
|
import * as Option from "effect/Option";
|
|
@@ -66,16 +67,21 @@ const preferMatchTagsExhaustive = define({
|
|
|
66
67
|
}),
|
|
67
68
|
create: function* () {
|
|
68
69
|
const context = yield* RuleContext;
|
|
70
|
+
const effectImports = /* @__PURE__ */ new Set();
|
|
69
71
|
return {
|
|
72
|
+
ImportDeclaration: (node) => {
|
|
73
|
+
if (isEffectPackageImport(node)) effectImports.add(node.source.value);
|
|
74
|
+
return Effect.void;
|
|
75
|
+
},
|
|
70
76
|
SwitchStatement: (node) => {
|
|
71
|
-
if (!isCompleteSwitchTransformation(node) || isInsideCatchAllHandler(node)) return Effect.void;
|
|
77
|
+
if (effectImports.size === 0 || !isCompleteSwitchTransformation(node) || isInsideCatchAllHandler(node)) return Effect.void;
|
|
72
78
|
return context.report(make({
|
|
73
79
|
node,
|
|
74
80
|
message: "Use Match.type with Match.tagsExhaustive so a new tagged variant requires a new case."
|
|
75
81
|
}));
|
|
76
82
|
},
|
|
77
83
|
IfStatement: (node) => {
|
|
78
|
-
if (!isCompleteIfTransformation(node) && !isCompleteSequentialIfTransformation(node) || isInsideCatchAllHandler(node)) return Effect.void;
|
|
84
|
+
if (effectImports.size === 0 || !isCompleteIfTransformation(node) && !isCompleteSequentialIfTransformation(node) || isInsideCatchAllHandler(node)) return Effect.void;
|
|
79
85
|
return context.report(make({
|
|
80
86
|
node,
|
|
81
87
|
message: "Use Match.type with Match.tagsExhaustive so a new tagged variant requires a new branch."
|
|
@@ -2,6 +2,7 @@ import { make } from "../vendor/effect-oxlint/Diagnostic.js";
|
|
|
2
2
|
import { RuleContext } from "../vendor/effect-oxlint/RuleContext.js";
|
|
3
3
|
import { define, meta } from "../vendor/effect-oxlint/Rule.js";
|
|
4
4
|
import "../vendor/effect-oxlint/index.js";
|
|
5
|
+
import { isEffectPackageImport } from "./_effect-namespaces.js";
|
|
5
6
|
import { effectCallbackLocation, hasOneTaggedSubject, tagComparisonsInOr } from "./_tagged-control-flow.js";
|
|
6
7
|
import * as Option from "effect/Option";
|
|
7
8
|
import * as Effect from "effect/Effect";
|
|
@@ -19,16 +20,23 @@ const preferPredicateIsTagged = define({
|
|
|
19
20
|
}),
|
|
20
21
|
create: function* () {
|
|
21
22
|
const context = yield* RuleContext;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
const effectImports = /* @__PURE__ */ new Set();
|
|
24
|
+
return {
|
|
25
|
+
ImportDeclaration: (node) => {
|
|
26
|
+
if (isEffectPackageImport(node)) effectImports.add(node.source.value);
|
|
27
|
+
return Effect.void;
|
|
28
|
+
},
|
|
29
|
+
LogicalExpression: (node) => {
|
|
30
|
+
if (effectImports.size === 0 || node.operator !== "||") return Effect.void;
|
|
31
|
+
if (node.parent?.type === "LogicalExpression" && node.parent.operator === "||") return Effect.void;
|
|
32
|
+
if (isCatchIfPredicate(node)) return Effect.void;
|
|
33
|
+
if (!Option.exists(tagComparisonsInOr(node), hasOneTaggedSubject)) return Effect.void;
|
|
34
|
+
return context.report(make({
|
|
35
|
+
node,
|
|
36
|
+
message: "Use a named Predicate refinement composed from Predicate.isTagged for multiple tags."
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
32
40
|
}
|
|
33
41
|
});
|
|
34
42
|
//#endregion
|