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,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.succeed("...")` with string literal arguments.
|
|
3
|
-
*
|
|
4
|
-
* Return domain values (Option/Either/tagged unions) instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-string-sentinel-return
|
|
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 noEffectSucceedString = Rule.define({
|
|
13
|
-
name: "no-effect-succeed-string",
|
|
14
|
-
meta: Rule.meta({
|
|
15
|
-
type: "suggestion",
|
|
16
|
-
description:
|
|
17
|
-
"Avoid Effect.succeed with string literals. Return domain values (tagged unions, Option, Either).",
|
|
18
|
-
}),
|
|
19
|
-
create: function* () {
|
|
20
|
-
const ctx = yield* RuleContext
|
|
21
|
-
return {
|
|
22
|
-
CallExpression: (node) => {
|
|
23
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "succeed")) return Effect.void
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
"arguments" in node &&
|
|
27
|
-
Array.isArray(node.arguments) &&
|
|
28
|
-
node.arguments.length === 1
|
|
29
|
-
) {
|
|
30
|
-
const arg = node.arguments[0]
|
|
31
|
-
if (
|
|
32
|
-
arg != null &&
|
|
33
|
-
typeof arg === "object" &&
|
|
34
|
-
"type" in arg &&
|
|
35
|
-
arg.type === "Literal" &&
|
|
36
|
-
"value" in arg &&
|
|
37
|
-
typeof arg.value === "string"
|
|
38
|
-
) {
|
|
39
|
-
return ctx.report(
|
|
40
|
-
Diagnostic.make({
|
|
41
|
-
node,
|
|
42
|
-
message:
|
|
43
|
-
"Avoid Effect.succeed with string literals. Use tagged unions, Option, or Either.",
|
|
44
|
-
}),
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return Effect.void
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
})
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.succeed(someVariable)` where the argument is a plain identifier.
|
|
3
|
-
*
|
|
4
|
-
* Suggests selecting a plain value (Option/Match), then running one Effect pipeline.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-succeed-variable
|
|
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 noEffectSucceedVariable = Rule.define({
|
|
13
|
-
name: "no-effect-succeed-variable",
|
|
14
|
-
meta: Rule.meta({
|
|
15
|
-
type: "suggestion",
|
|
16
|
-
description:
|
|
17
|
-
"Avoid Effect.succeed with a plain variable. Select value with Option/Match, then run one Effect pipeline.",
|
|
18
|
-
}),
|
|
19
|
-
create: function* () {
|
|
20
|
-
const ctx = yield* RuleContext
|
|
21
|
-
return {
|
|
22
|
-
CallExpression: (node) => {
|
|
23
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "succeed")) return Effect.void
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
"arguments" in node &&
|
|
27
|
-
Array.isArray(node.arguments) &&
|
|
28
|
-
node.arguments.length === 1
|
|
29
|
-
) {
|
|
30
|
-
const arg = node.arguments[0]
|
|
31
|
-
if (
|
|
32
|
-
arg != null &&
|
|
33
|
-
typeof arg === "object" &&
|
|
34
|
-
"type" in arg &&
|
|
35
|
-
arg.type === "Identifier"
|
|
36
|
-
) {
|
|
37
|
-
return ctx.report(
|
|
38
|
-
Diagnostic.make({
|
|
39
|
-
node,
|
|
40
|
-
message:
|
|
41
|
-
"Avoid Effect.succeed(variable). Select value with Option/Match, then run one Effect pipeline.",
|
|
42
|
-
}),
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return Effect.void
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
})
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.succeed(undefined)` and `Effect.succeed(void 0)`.
|
|
3
|
-
*
|
|
4
|
-
* Use `Effect.void` instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/effectSucceedWithVoid
|
|
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 isUndefinedLike = (node: unknown): boolean => {
|
|
13
|
-
if (node == null || typeof node !== "object" || !("type" in node)) return false
|
|
14
|
-
// undefined identifier
|
|
15
|
-
if (node.type === "Identifier" && "name" in node && node.name === "undefined") return true
|
|
16
|
-
// void 0
|
|
17
|
-
if (
|
|
18
|
-
node.type === "UnaryExpression" &&
|
|
19
|
-
"operator" in node &&
|
|
20
|
-
node.operator === "void" &&
|
|
21
|
-
"argument" in node &&
|
|
22
|
-
node.argument != null &&
|
|
23
|
-
typeof node.argument === "object" &&
|
|
24
|
-
"type" in node.argument &&
|
|
25
|
-
node.argument.type === "Literal" &&
|
|
26
|
-
"value" in node.argument &&
|
|
27
|
-
node.argument.value === 0
|
|
28
|
-
)
|
|
29
|
-
return true
|
|
30
|
-
return false
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const noEffectSucceedVoid = Rule.define({
|
|
34
|
-
name: "no-effect-succeed-void",
|
|
35
|
-
meta: Rule.meta({
|
|
36
|
-
type: "suggestion",
|
|
37
|
-
description:
|
|
38
|
-
"Avoid Effect.succeed(undefined). Use Effect.void instead.",
|
|
39
|
-
}),
|
|
40
|
-
create: function* () {
|
|
41
|
-
const ctx = yield* RuleContext
|
|
42
|
-
return {
|
|
43
|
-
CallExpression: (node) => {
|
|
44
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "succeed")) return Effect.void
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
"arguments" in node &&
|
|
48
|
-
Array.isArray(node.arguments) &&
|
|
49
|
-
node.arguments.length === 1 &&
|
|
50
|
-
isUndefinedLike(node.arguments[0])
|
|
51
|
-
) {
|
|
52
|
-
return ctx.report(
|
|
53
|
-
Diagnostic.make({
|
|
54
|
-
node,
|
|
55
|
-
message: "Use Effect.void instead of Effect.succeed(undefined).",
|
|
56
|
-
}),
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
return Effect.void
|
|
60
|
-
},
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
})
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `console.*` inside `Effect.sync(...)`.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect.log* instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-sync-console
|
|
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 consoleMethods = new Set(["log", "warn", "error", "info", "debug", "trace"])
|
|
13
|
-
|
|
14
|
-
const containsConsoleCall = (node: unknown): boolean => {
|
|
15
|
-
if (node == null || typeof node !== "object") return false
|
|
16
|
-
const n = node as Record<string, unknown>
|
|
17
|
-
if (
|
|
18
|
-
n["type"] === "CallExpression" &&
|
|
19
|
-
"callee" in n &&
|
|
20
|
-
n["callee"] != null &&
|
|
21
|
-
typeof n["callee"] === "object"
|
|
22
|
-
) {
|
|
23
|
-
const callee = n["callee"] as Record<string, unknown>
|
|
24
|
-
if (
|
|
25
|
-
callee["type"] === "MemberExpression" &&
|
|
26
|
-
"object" in callee &&
|
|
27
|
-
"property" in callee
|
|
28
|
-
) {
|
|
29
|
-
const obj = callee["object"] as Record<string, unknown> | null
|
|
30
|
-
const prop = callee["property"] as Record<string, unknown> | null
|
|
31
|
-
if (
|
|
32
|
-
obj != null && obj["type"] === "Identifier" && obj["name"] === "console" &&
|
|
33
|
-
prop != null && prop["type"] === "Identifier" && typeof prop["name"] === "string" &&
|
|
34
|
-
consoleMethods.has(prop["name"])
|
|
35
|
-
) {
|
|
36
|
-
return true
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
// Recurse into body/arguments
|
|
41
|
-
if ("body" in n) {
|
|
42
|
-
if (Array.isArray(n["body"])) {
|
|
43
|
-
for (const child of n["body"]) {
|
|
44
|
-
if (containsConsoleCall(child)) return true
|
|
45
|
-
}
|
|
46
|
-
} else if (containsConsoleCall(n["body"])) return true
|
|
47
|
-
}
|
|
48
|
-
if ("argument" in n && containsConsoleCall(n["argument"])) return true
|
|
49
|
-
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
50
|
-
for (const arg of n["arguments"]) {
|
|
51
|
-
if (containsConsoleCall(arg)) return true
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if ("expression" in n && containsConsoleCall(n["expression"])) return true
|
|
55
|
-
if ("expressions" in n && Array.isArray(n["expressions"])) {
|
|
56
|
-
for (const expr of n["expressions"]) {
|
|
57
|
-
if (containsConsoleCall(expr)) return true
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return false
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export const noEffectSyncConsole = Rule.define({
|
|
64
|
-
name: "no-effect-sync-console",
|
|
65
|
-
meta: Rule.meta({
|
|
66
|
-
type: "suggestion",
|
|
67
|
-
description: "Avoid console.* inside Effect.sync. Use Effect.log* instead.",
|
|
68
|
-
}),
|
|
69
|
-
create: function* () {
|
|
70
|
-
const ctx = yield* RuleContext
|
|
71
|
-
return {
|
|
72
|
-
CallExpression: (node) => {
|
|
73
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "sync")) return Effect.void
|
|
74
|
-
|
|
75
|
-
if ("arguments" in node && Array.isArray(node.arguments)) {
|
|
76
|
-
for (const arg of node.arguments) {
|
|
77
|
-
if (containsConsoleCall(arg)) {
|
|
78
|
-
return ctx.report(
|
|
79
|
-
Diagnostic.make({
|
|
80
|
-
node,
|
|
81
|
-
message: "Avoid console.* inside Effect.sync. Use Effect.log, Effect.logWarning, etc.",
|
|
82
|
-
}),
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return Effect.void
|
|
88
|
-
},
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
})
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Warn on `Effect.sync(() => someCall(...))` wrapping a plain function call.
|
|
3
|
-
*
|
|
4
|
-
* Consider whether the wrapped call truly needs to be in Effect.sync.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/warn-effect-sync-wrapper
|
|
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 noEffectSyncWrapper = Rule.define({
|
|
13
|
-
name: "no-effect-sync-wrapper",
|
|
14
|
-
meta: Rule.meta({
|
|
15
|
-
type: "suggestion",
|
|
16
|
-
description: "Effect.sync wrapping a plain call. Consider if Effect.sync is necessary.",
|
|
17
|
-
}),
|
|
18
|
-
create: function* () {
|
|
19
|
-
const ctx = yield* RuleContext
|
|
20
|
-
return {
|
|
21
|
-
CallExpression: (node) => {
|
|
22
|
-
if (!AST.isCallOf(node as ESTree.CallExpression, "Effect", "sync")) return Effect.void
|
|
23
|
-
|
|
24
|
-
if (!("arguments" in node) || !Array.isArray(node.arguments) || node.arguments.length < 1)
|
|
25
|
-
return Effect.void
|
|
26
|
-
|
|
27
|
-
const arg = node.arguments[0]
|
|
28
|
-
if (arg == null || typeof arg !== "object" || !("type" in arg)) return Effect.void
|
|
29
|
-
|
|
30
|
-
// () => someCall(...)
|
|
31
|
-
if (arg.type === "ArrowFunctionExpression" && "body" in arg) {
|
|
32
|
-
const body = arg.body
|
|
33
|
-
if (
|
|
34
|
-
body != null &&
|
|
35
|
-
typeof body === "object" &&
|
|
36
|
-
"type" in body &&
|
|
37
|
-
body.type === "CallExpression"
|
|
38
|
-
) {
|
|
39
|
-
// Exclude console.* (handled by no-effect-sync-console)
|
|
40
|
-
const callee = (body as unknown as Record<string, unknown>)["callee"]
|
|
41
|
-
if (
|
|
42
|
-
callee != null &&
|
|
43
|
-
typeof callee === "object" &&
|
|
44
|
-
"type" in callee &&
|
|
45
|
-
callee.type === "MemberExpression" &&
|
|
46
|
-
"object" in callee &&
|
|
47
|
-
callee.object != null &&
|
|
48
|
-
typeof callee.object === "object" &&
|
|
49
|
-
"name" in callee.object &&
|
|
50
|
-
callee.object.name === "console"
|
|
51
|
-
) {
|
|
52
|
-
return Effect.void
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return ctx.report(
|
|
56
|
-
Diagnostic.make({
|
|
57
|
-
node,
|
|
58
|
-
message: "Effect.sync wrapping a plain function call. Consider if Effect.sync is necessary.",
|
|
59
|
-
}),
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return Effect.void
|
|
64
|
-
},
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
})
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `type X = Effect.Effect<...>` type aliases.
|
|
3
|
-
*
|
|
4
|
-
* Keep Effect types on service methods or inline at call site.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-type-alias
|
|
7
|
-
*/
|
|
8
|
-
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import * as Effect from "effect/Effect"
|
|
10
|
-
|
|
11
|
-
const containsEffectType = (node: unknown): boolean => {
|
|
12
|
-
if (node == null || typeof node !== "object") return false
|
|
13
|
-
const n = node as Record<string, unknown>
|
|
14
|
-
// TSTypeReference with Effect.Effect
|
|
15
|
-
if (
|
|
16
|
-
n["type"] === "TSTypeReference" &&
|
|
17
|
-
"typeName" in n &&
|
|
18
|
-
n["typeName"] != null &&
|
|
19
|
-
typeof n["typeName"] === "object"
|
|
20
|
-
) {
|
|
21
|
-
const typeName = n["typeName"] as Record<string, unknown>
|
|
22
|
-
if (
|
|
23
|
-
typeName["type"] === "TSQualifiedName" &&
|
|
24
|
-
"left" in typeName && "right" in typeName
|
|
25
|
-
) {
|
|
26
|
-
const left = typeName["left"] as Record<string, unknown> | null
|
|
27
|
-
const right = typeName["right"] as Record<string, unknown> | null
|
|
28
|
-
if (
|
|
29
|
-
left != null && left["type"] === "Identifier" && left["name"] === "Effect" &&
|
|
30
|
-
right != null && right["type"] === "Identifier" && right["name"] === "Effect"
|
|
31
|
-
) {
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// Recurse into typeAnnotation, typeParameters
|
|
37
|
-
if ("typeAnnotation" in n && containsEffectType(n["typeAnnotation"])) return true
|
|
38
|
-
if ("typeParameters" in n && n["typeParameters"] != null && typeof n["typeParameters"] === "object") {
|
|
39
|
-
const tp = n["typeParameters"] as Record<string, unknown>
|
|
40
|
-
if ("params" in tp && Array.isArray(tp["params"])) {
|
|
41
|
-
for (const p of tp["params"]) {
|
|
42
|
-
if (containsEffectType(p)) return true
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return false
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const noEffectTypeAlias = Rule.define({
|
|
50
|
-
name: "no-effect-type-alias",
|
|
51
|
-
meta: Rule.meta({
|
|
52
|
-
type: "suggestion",
|
|
53
|
-
description: "Avoid Effect type aliases. Keep Effect types on service methods or inline.",
|
|
54
|
-
}),
|
|
55
|
-
create: function* () {
|
|
56
|
-
const ctx = yield* RuleContext
|
|
57
|
-
return {
|
|
58
|
-
TSTypeAliasDeclaration: (node) => {
|
|
59
|
-
if ("typeAnnotation" in node && containsEffectType(node.typeAnnotation)) {
|
|
60
|
-
return ctx.report(
|
|
61
|
-
Diagnostic.make({
|
|
62
|
-
node,
|
|
63
|
-
message: "Avoid type alias wrapping Effect.Effect. Let Effect types flow from service methods.",
|
|
64
|
-
}),
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
return Effect.void
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
})
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban wrapper functions that just alias an Effect.fn or pipe(Effect.fn(...)).
|
|
3
|
-
*
|
|
4
|
-
* Inline the pipeline at the call site or define a real domain function.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-effect-wrapper-alias
|
|
7
|
-
*/
|
|
8
|
-
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import * as Effect from "effect/Effect"
|
|
10
|
-
|
|
11
|
-
const isEffectFnCall = (node: unknown): boolean => {
|
|
12
|
-
if (node == null || typeof node !== "object") return false
|
|
13
|
-
const n = node 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 Record<string, unknown>
|
|
18
|
-
if (c["type"] !== "MemberExpression") return false
|
|
19
|
-
const obj = c["object"] as Record<string, unknown> | null
|
|
20
|
-
const prop = c["property"] as Record<string, unknown> | null
|
|
21
|
-
return (
|
|
22
|
-
obj != null && obj["type"] === "Identifier" && obj["name"] === "Effect" &&
|
|
23
|
-
prop != null && prop["type"] === "Identifier" && prop["name"] === "fn"
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const isPipeWrappingEffectFn = (node: unknown): boolean => {
|
|
28
|
-
if (node == null || typeof node !== "object") return false
|
|
29
|
-
const n = node as Record<string, unknown>
|
|
30
|
-
if (n["type"] !== "CallExpression") return false
|
|
31
|
-
const callee = n["callee"]
|
|
32
|
-
if (callee == null || typeof callee !== "object") return false
|
|
33
|
-
const c = callee as Record<string, unknown>
|
|
34
|
-
if (!(c["type"] === "Identifier" && c["name"] === "pipe")) return false
|
|
35
|
-
const args = n["arguments"]
|
|
36
|
-
if (!Array.isArray(args) || args.length === 0) return false
|
|
37
|
-
return isEffectFnCall(args[0])
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const noEffectWrapperAlias = Rule.define({
|
|
41
|
-
name: "no-effect-wrapper-alias",
|
|
42
|
-
meta: Rule.meta({
|
|
43
|
-
type: "suggestion",
|
|
44
|
-
description: "Avoid wrapper functions aliasing Effect.fn. Inline or define a real domain function.",
|
|
45
|
-
}),
|
|
46
|
-
create: function* () {
|
|
47
|
-
const ctx = yield* RuleContext
|
|
48
|
-
return {
|
|
49
|
-
VariableDeclarator: (node) => {
|
|
50
|
-
if (!("init" in node) || node.init == null) return Effect.void
|
|
51
|
-
const init = node.init
|
|
52
|
-
|
|
53
|
-
// const x = Effect.fn(...)
|
|
54
|
-
if (isEffectFnCall(init)) {
|
|
55
|
-
return ctx.report(
|
|
56
|
-
Diagnostic.make({
|
|
57
|
-
node,
|
|
58
|
-
message: "Avoid aliasing Effect.fn. Inline the pipeline or define a real domain function.",
|
|
59
|
-
}),
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// const x = pipe(Effect.fn(...), ...)
|
|
64
|
-
if (isPipeWrappingEffectFn(init)) {
|
|
65
|
-
return ctx.report(
|
|
66
|
-
Diagnostic.make({
|
|
67
|
-
node,
|
|
68
|
-
message: "Avoid wrapping Effect.fn in pipe. Inline the pipeline or define a real domain function.",
|
|
69
|
-
}),
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// const x = () => Effect.fn(...) or const x = () => pipe(Effect.fn(...))
|
|
74
|
-
if (
|
|
75
|
-
typeof init === "object" &&
|
|
76
|
-
"type" in init &&
|
|
77
|
-
(init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") &&
|
|
78
|
-
"body" in init
|
|
79
|
-
) {
|
|
80
|
-
const body = init.body
|
|
81
|
-
if (isEffectFnCall(body) || isPipeWrappingEffectFn(body)) {
|
|
82
|
-
return ctx.report(
|
|
83
|
-
Diagnostic.make({
|
|
84
|
-
node,
|
|
85
|
-
message: "Avoid wrapper function aliasing Effect.fn. Inline or define a real domain function.",
|
|
86
|
-
}),
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return Effect.void
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
})
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `class Foo extends Error`.
|
|
3
|
-
*
|
|
4
|
-
* Use Schema.TaggedErrorClass or Data.TaggedError instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/extendsNativeError
|
|
7
|
-
*/
|
|
8
|
-
import { Diagnostic, Rule, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import * as Effect from "effect/Effect"
|
|
10
|
-
|
|
11
|
-
const nativeErrors = new Set([
|
|
12
|
-
"Error",
|
|
13
|
-
"TypeError",
|
|
14
|
-
"RangeError",
|
|
15
|
-
"ReferenceError",
|
|
16
|
-
"SyntaxError",
|
|
17
|
-
"URIError",
|
|
18
|
-
"EvalError",
|
|
19
|
-
])
|
|
20
|
-
|
|
21
|
-
export const noExtendsNativeError = Rule.define({
|
|
22
|
-
name: "no-extends-native-error",
|
|
23
|
-
meta: Rule.meta({
|
|
24
|
-
type: "suggestion",
|
|
25
|
-
description:
|
|
26
|
-
"Avoid extending native Error. Use Schema.TaggedErrorClass or Data.TaggedError.",
|
|
27
|
-
}),
|
|
28
|
-
create: function* () {
|
|
29
|
-
const ctx = yield* RuleContext
|
|
30
|
-
return {
|
|
31
|
-
ClassDeclaration: (node) => {
|
|
32
|
-
if (
|
|
33
|
-
"superClass" in node &&
|
|
34
|
-
node.superClass != null &&
|
|
35
|
-
typeof node.superClass === "object" &&
|
|
36
|
-
"type" in node.superClass &&
|
|
37
|
-
node.superClass.type === "Identifier" &&
|
|
38
|
-
"name" in node.superClass &&
|
|
39
|
-
typeof node.superClass.name === "string" &&
|
|
40
|
-
nativeErrors.has(node.superClass.name)
|
|
41
|
-
) {
|
|
42
|
-
return ctx.report(
|
|
43
|
-
Diagnostic.make({
|
|
44
|
-
node,
|
|
45
|
-
message:
|
|
46
|
-
"Avoid extending native Error. Use Schema.TaggedErrorClass or Data.TaggedError.",
|
|
47
|
-
}),
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
return Effect.void
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
})
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban nested `Effect.flatMap` calls (flatMap ladder).
|
|
3
|
-
*
|
|
4
|
-
* Build context once with `Effect.all`/`Effect.map` and run a single flatMap.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-flatmap-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 isEffectFlatMap = (node: ESTree.CallExpression): boolean =>
|
|
13
|
-
AST.isCallOf(node, "Effect", "flatMap")
|
|
14
|
-
|
|
15
|
-
const containsEffectFlatMap = (node: unknown): boolean => {
|
|
16
|
-
if (node == null || typeof node !== "object") return false
|
|
17
|
-
const n = node as Record<string, unknown>
|
|
18
|
-
if (n["type"] === "CallExpression" && "callee" in n && "arguments" in n) {
|
|
19
|
-
if (isEffectFlatMap(node as ESTree.CallExpression)) return true
|
|
20
|
-
}
|
|
21
|
-
if ("arguments" in n && Array.isArray(n["arguments"])) {
|
|
22
|
-
for (const arg of n["arguments"]) {
|
|
23
|
-
if (containsEffectFlatMap(arg)) return true
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if ("body" in n) return containsEffectFlatMap(n["body"])
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const noFlatmapLadder = Rule.define({
|
|
31
|
-
name: "no-flatmap-ladder",
|
|
32
|
-
meta: Rule.meta({
|
|
33
|
-
type: "suggestion",
|
|
34
|
-
description:
|
|
35
|
-
"Avoid nested Effect.flatMap (flatMap ladder). Build context once with Effect.all and run a single flatMap.",
|
|
36
|
-
}),
|
|
37
|
-
create: function* () {
|
|
38
|
-
const ctx = yield* RuleContext
|
|
39
|
-
return {
|
|
40
|
-
CallExpression: (node) => {
|
|
41
|
-
const ce = node as ESTree.CallExpression
|
|
42
|
-
if (!isEffectFlatMap(ce)) return Effect.void
|
|
43
|
-
if (!("arguments" in ce) || !Array.isArray(ce.arguments)) return Effect.void
|
|
44
|
-
|
|
45
|
-
// Check if any argument's subtree contains another Effect.flatMap
|
|
46
|
-
for (const arg of ce.arguments) {
|
|
47
|
-
if (arg != null && typeof arg === "object" && "type" in arg) {
|
|
48
|
-
if (arg.type === "CallExpression" && isEffectFlatMap(arg as ESTree.CallExpression)) {
|
|
49
|
-
return ctx.report(
|
|
50
|
-
Diagnostic.make({
|
|
51
|
-
node,
|
|
52
|
-
message:
|
|
53
|
-
"Nested Effect.flatMap detected. Build context with Effect.all, then single flatMap.",
|
|
54
|
-
}),
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
// Check inside arrow/function body
|
|
58
|
-
if (
|
|
59
|
-
(arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression") &&
|
|
60
|
-
"body" in arg &&
|
|
61
|
-
containsEffectFlatMap(arg.body)
|
|
62
|
-
) {
|
|
63
|
-
return ctx.report(
|
|
64
|
-
Diagnostic.make({
|
|
65
|
-
node,
|
|
66
|
-
message:
|
|
67
|
-
"Nested Effect.flatMap detected. Build context with Effect.all, then single flatMap.",
|
|
68
|
-
}),
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return Effect.void
|
|
74
|
-
},
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
})
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Option.fromNullable(x ?? null)` / `Option.fromNullable(x ?? undefined)`.
|
|
3
|
-
*
|
|
4
|
-
* The nullish coalescing is redundant — pass the source directly.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-fromnullable-nullish-coalesce
|
|
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 noFromNullableCoalesce = Rule.define({
|
|
13
|
-
name: "no-fromnullable-coalesce",
|
|
14
|
-
meta: Rule.meta({
|
|
15
|
-
type: "suggestion",
|
|
16
|
-
description:
|
|
17
|
-
"Redundant nullish coalescing in Option.fromNullable. Pass the value directly.",
|
|
18
|
-
}),
|
|
19
|
-
create: function* () {
|
|
20
|
-
const ctx = yield* RuleContext
|
|
21
|
-
return {
|
|
22
|
-
CallExpression: (node) => {
|
|
23
|
-
// Match Option.fromNullable(...) or Option.fromNullishOr(...)
|
|
24
|
-
const ce = node as ESTree.CallExpression
|
|
25
|
-
const isFromNullable =
|
|
26
|
-
AST.isCallOf(ce, "Option", "fromNullable") ||
|
|
27
|
-
AST.isCallOf(ce, "Option", "fromNullishOr")
|
|
28
|
-
if (!isFromNullable) return Effect.void
|
|
29
|
-
|
|
30
|
-
if ("arguments" in node && Array.isArray(node.arguments) && node.arguments.length >= 1) {
|
|
31
|
-
const arg = node.arguments[0]
|
|
32
|
-
if (
|
|
33
|
-
arg != null &&
|
|
34
|
-
typeof arg === "object" &&
|
|
35
|
-
"type" in arg &&
|
|
36
|
-
arg.type === "LogicalExpression" &&
|
|
37
|
-
"operator" in arg &&
|
|
38
|
-
arg.operator === "??"
|
|
39
|
-
) {
|
|
40
|
-
// Check RHS is null or undefined
|
|
41
|
-
if ("right" in arg && arg.right != null && typeof arg.right === "object" && "type" in arg.right) {
|
|
42
|
-
const rhs = arg.right
|
|
43
|
-
const isNullish =
|
|
44
|
-
(rhs.type === "Literal" && "value" in rhs && rhs.value === null) ||
|
|
45
|
-
(rhs.type === "Identifier" && "name" in rhs && rhs.name === "undefined")
|
|
46
|
-
if (isNullish) {
|
|
47
|
-
return ctx.report(
|
|
48
|
-
Diagnostic.make({
|
|
49
|
-
node,
|
|
50
|
-
message:
|
|
51
|
-
"Redundant ?? null/undefined in Option.fromNullable. Pass the value directly.",
|
|
52
|
-
}),
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return Effect.void
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
})
|