oxlint-plugin-effect 0.0.0 → 0.2.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/CHANGELOG.md +55 -0
- package/package.json +6 -7
- package/src/presets/core.ts +24 -13
- package/src/presets/effect-native.ts +18 -25
- package/src/presets/full.ts +33 -47
- package/src/presets/functional.ts +18 -0
- package/src/presets/index.ts +2 -0
- package/src/presets/strict.ts +77 -0
- package/src/rules/index.ts +18 -29
- package/src/rules/no-arrow-ladder.ts +1 -2
- package/src/rules/no-async-function.ts +1 -2
- package/src/rules/no-catch-all-to-map-error.ts +90 -0
- package/src/rules/no-effect-fn-generator.ts +1 -2
- package/src/rules/no-effect-gen-adapter.ts +2 -2
- package/src/rules/no-effect-map-flatten.ts +82 -0
- package/src/rules/no-effect-map-void.ts +1 -2
- package/src/rules/no-effect-orElse-ladder.ts +1 -2
- package/src/rules/no-effect-succeed-string.ts +1 -2
- package/src/rules/no-effect-succeed-variable.ts +1 -2
- package/src/rules/no-effect-succeed-void.ts +1 -2
- package/src/rules/no-effect-sync-console.ts +1 -2
- package/src/rules/no-effect-sync-wrapper.ts +1 -2
- package/src/rules/no-effect-type-alias.ts +1 -2
- package/src/rules/no-effect-wrapper-alias.ts +1 -4
- package/src/rules/no-extends-native-error.ts +1 -2
- package/src/rules/no-flatmap-ladder.ts +1 -3
- package/src/rules/no-fromnullable-coalesce.ts +1 -2
- package/src/rules/no-global-error-in-catch.ts +104 -0
- package/src/rules/no-global-error-in-failure.ts +83 -0
- package/src/rules/no-globals.ts +178 -0
- package/src/rules/no-iife-wrapper.ts +1 -2
- package/src/rules/{no-inline-runtime-provide.ts → no-inline-provide.ts} +3 -4
- package/src/rules/no-instanceof-schema.ts +2 -3
- package/src/rules/no-manual-effect-channels.ts +1 -2
- package/src/rules/no-match-effect-branch.ts +1 -2
- package/src/rules/no-match-void-branch.ts +1 -2
- package/src/rules/no-multiple-effect-provide.ts +75 -0
- package/src/rules/no-nested-effect-call.ts +1 -3
- package/src/rules/no-nested-effect-gen.ts +1 -2
- package/src/rules/no-nested-pipe.ts +1 -4
- package/src/rules/no-new-error.ts +46 -10
- package/src/rules/no-new-promise.ts +43 -7
- package/src/rules/no-node-builtin-import.ts +1 -3
- package/src/rules/no-option-boolean-normalization.ts +1 -2
- package/src/rules/no-positional-log-error.ts +76 -0
- package/src/rules/no-redundant-schema-tag-identifier.ts +62 -0
- package/src/rules/no-return-in-arrow.ts +1 -2
- package/src/rules/no-return-nullish.ts +80 -0
- package/src/rules/no-run-in-effect-gen.ts +66 -0
- package/src/rules/no-runtime-run-fork.ts +1 -1
- package/src/rules/no-schema-struct-with-tag.ts +64 -0
- package/src/rules/no-schema-union-of-literals.ts +48 -0
- package/src/rules/no-string-sentinel-const.ts +1 -2
- package/src/rules/no-ternary.ts +1 -3
- package/src/rules/no-throw-statement.ts +32 -6
- package/src/rules/no-try-catch.ts +32 -7
- package/src/rules/no-unnecessary-arrow-block.ts +1 -2
- package/src/rules/no-unnecessary-effect-gen.ts +1 -3
- package/src/rules/no-unnecessary-pipe-chain.ts +47 -0
- package/src/rules/no-unnecessary-pipe.ts +1 -2
- package/src/vendor/effect-oxlint/Rule.ts +141 -183
- package/tests/ban-rules.test.ts +0 -184
- package/tests/context-rules.test.ts +127 -145
- package/tests/pattern-rules.test.ts +39 -50
- package/src/rules/no-console-in-effect.ts +0 -46
- package/src/rules/no-date-in-effect.ts +0 -61
- package/src/rules/no-dynamic-import.ts +0 -13
- package/src/rules/no-fetch-in-effect.ts +0 -47
- package/src/rules/no-global-console.ts +0 -17
- package/src/rules/no-global-date.ts +0 -20
- package/src/rules/no-global-fetch.ts +0 -14
- package/src/rules/no-global-random.ts +0 -13
- package/src/rules/no-global-timers.ts +0 -17
- package/src/rules/no-json-in-effect.ts +0 -45
- package/src/rules/no-json-parse.ts +0 -18
- package/src/rules/no-platform-globals.ts +0 -117
- package/src/rules/no-process-env-in-effect.ts +0 -39
- package/src/rules/no-process-env.ts +0 -13
- package/src/rules/no-random-in-effect.ts +0 -39
- package/src/rules/no-return-null.ts +0 -47
- package/src/rules/no-run-in-effect.ts +0 -18
- package/src/rules/no-throw-in-effect-gen.ts +0 -42
- package/src/rules/no-timers-in-effect.ts +0 -47
- package/src/rules/no-try-catch-in-effect-gen.ts +0 -42
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `fetch()` inside Effect.gen/fn context.
|
|
3
|
-
*
|
|
4
|
-
* Use HttpClient from 'effect/unstable/http' instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalFetchInEffect
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
import * as Ref from "effect/Ref"
|
|
14
|
-
|
|
15
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
16
|
-
|
|
17
|
-
export const noFetchInEffect = Rule.define({
|
|
18
|
-
name: "no-fetch-in-effect",
|
|
19
|
-
meta: Rule.meta({
|
|
20
|
-
type: "suggestion",
|
|
21
|
-
description:
|
|
22
|
-
"Avoid fetch() inside Effect.gen/fn. Use HttpClient from 'effect/unstable/http'.",
|
|
23
|
-
}),
|
|
24
|
-
create: function* () {
|
|
25
|
-
const ctx = yield* RuleContext
|
|
26
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
27
|
-
|
|
28
|
-
return Visitor.merge(tracker, {
|
|
29
|
-
CallExpression: (node) =>
|
|
30
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
31
|
-
if (d > 0) {
|
|
32
|
-
const name = Option.getOrUndefined(AST.calleeName(node as ESTree.CallExpression))
|
|
33
|
-
if (name === "fetch") {
|
|
34
|
-
return ctx.report(
|
|
35
|
-
Diagnostic.make({
|
|
36
|
-
node,
|
|
37
|
-
message:
|
|
38
|
-
"Avoid fetch() inside Effect context. Use HttpClient from 'effect/unstable/http'.",
|
|
39
|
-
}),
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return Effect.void
|
|
44
|
-
}),
|
|
45
|
-
})
|
|
46
|
-
},
|
|
47
|
-
})
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `console.log/warn/error/info/debug/trace`.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect Logger service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalConsole
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noGlobalConsole = Rule.banMember(
|
|
11
|
-
"console",
|
|
12
|
-
["log", "warn", "error", "info", "debug", "trace"],
|
|
13
|
-
{
|
|
14
|
-
message: "Avoid console.*. Use Effect Logger service.",
|
|
15
|
-
meta: { type: "suggestion" },
|
|
16
|
-
},
|
|
17
|
-
)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Date.now()` and `new Date()`.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect Clock service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalDate
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noGlobalDate = Rule.banMultiple(
|
|
11
|
-
{
|
|
12
|
-
newExprs: "Date",
|
|
13
|
-
members: [["Date", "now"]],
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
name: "no-global-date",
|
|
17
|
-
message: "Avoid Date.now() and new Date(). Use Clock service.",
|
|
18
|
-
meta: { type: "suggestion" },
|
|
19
|
-
},
|
|
20
|
-
)
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban bare `fetch()` calls.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect HttpClient service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalFetch
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noGlobalFetch = Rule.banCallOf("fetch", {
|
|
11
|
-
message:
|
|
12
|
-
"Avoid global fetch. Use HttpClient from 'effect/unstable/http'.",
|
|
13
|
-
meta: { type: "suggestion" },
|
|
14
|
-
})
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Math.random()`.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect Random service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalRandom
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noGlobalRandom = Rule.banMember("Math", "random", {
|
|
11
|
-
message: "Avoid Math.random(). Use Random service.",
|
|
12
|
-
meta: { type: "suggestion" },
|
|
13
|
-
})
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `setTimeout` and `setInterval`.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect Schedule or Effect.sleep instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalTimers
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noGlobalTimers = Rule.banCallOf(
|
|
11
|
-
["setTimeout", "setInterval"],
|
|
12
|
-
{
|
|
13
|
-
message:
|
|
14
|
-
"Avoid setTimeout/setInterval. Use Effect.sleep or Schedule.",
|
|
15
|
-
meta: { type: "suggestion" },
|
|
16
|
-
},
|
|
17
|
-
)
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `JSON.parse`/`JSON.stringify` inside Effect.gen/fn context.
|
|
3
|
-
*
|
|
4
|
-
* Use Schema.fromJsonString for type-safe JSON.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/preferSchemaOverJson (inside Effect context)
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
import * as Ref from "effect/Ref"
|
|
14
|
-
|
|
15
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
16
|
-
|
|
17
|
-
export const noJsonInEffect = Rule.define({
|
|
18
|
-
name: "no-json-in-effect",
|
|
19
|
-
meta: Rule.meta({
|
|
20
|
-
type: "suggestion",
|
|
21
|
-
description: "Avoid JSON.parse/stringify inside Effect.gen/fn. Use Schema.fromJsonString.",
|
|
22
|
-
}),
|
|
23
|
-
create: function* () {
|
|
24
|
-
const ctx = yield* RuleContext
|
|
25
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
26
|
-
|
|
27
|
-
return Visitor.merge(tracker, {
|
|
28
|
-
MemberExpression: (node) =>
|
|
29
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
30
|
-
if (
|
|
31
|
-
d > 0 &&
|
|
32
|
-
Option.isSome(AST.matchMember(node as ESTree.MemberExpression, "JSON", ["parse", "stringify"]))
|
|
33
|
-
) {
|
|
34
|
-
return ctx.report(
|
|
35
|
-
Diagnostic.make({
|
|
36
|
-
node,
|
|
37
|
-
message: "Avoid JSON.parse/stringify inside Effect context. Use Schema.fromJsonString.",
|
|
38
|
-
}),
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
return Effect.void
|
|
42
|
-
}),
|
|
43
|
-
})
|
|
44
|
-
},
|
|
45
|
-
})
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `JSON.parse` and `JSON.stringify`.
|
|
3
|
-
*
|
|
4
|
-
* Use Schema.fromJsonString for type-safe JSON handling.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/preferSchemaOverJson
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noJsonParse = Rule.banMember(
|
|
11
|
-
"JSON",
|
|
12
|
-
["parse", "stringify"],
|
|
13
|
-
{
|
|
14
|
-
message:
|
|
15
|
-
"Avoid JSON.parse/stringify. Use Schema.fromJsonString for type-safe JSON.",
|
|
16
|
-
meta: { type: "suggestion" },
|
|
17
|
-
},
|
|
18
|
-
)
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban platform-specific global access inside Effect.gen/fn context.
|
|
3
|
-
*
|
|
4
|
-
* Catches `Bun.*`, `process.*`, `Deno.*` — any runtime-specific API
|
|
5
|
-
* that should be wrapped in an Effect service for testability and portability.
|
|
6
|
-
*
|
|
7
|
-
* Provides Effect alternative hints where known.
|
|
8
|
-
*
|
|
9
|
-
* Sources: language-service/processEnv, processEnvInEffect + generalized
|
|
10
|
-
*/
|
|
11
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
12
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
13
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
14
|
-
import * as Effect from "effect/Effect"
|
|
15
|
-
import * as Option from "effect/Option"
|
|
16
|
-
import * as Ref from "effect/Ref"
|
|
17
|
-
|
|
18
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
19
|
-
|
|
20
|
-
const platformObjects = new Set(["Bun", "process", "Deno"])
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Map of `Object.property` → Effect alternative hint.
|
|
24
|
-
* Falls back to a generic message for unknown properties.
|
|
25
|
-
*/
|
|
26
|
-
const alternatives: Record<string, Record<string, string>> = {
|
|
27
|
-
Bun: {
|
|
28
|
-
file: "Use FileSystem from 'effect'",
|
|
29
|
-
write: "Use FileSystem from 'effect'",
|
|
30
|
-
read: "Use FileSystem from 'effect'",
|
|
31
|
-
serve: "Use HttpServer from 'effect/unstable/http'",
|
|
32
|
-
env: "Use Config from 'effect'",
|
|
33
|
-
spawn: "Use Command from 'effect'",
|
|
34
|
-
sleep: "Use Effect.sleep",
|
|
35
|
-
stdin: "Use Terminal from 'effect'",
|
|
36
|
-
stdout: "Use Terminal from 'effect'",
|
|
37
|
-
stderr: "Use Terminal from 'effect'",
|
|
38
|
-
cwd: "Use Path from 'effect'",
|
|
39
|
-
which: "Use Command from 'effect'",
|
|
40
|
-
resolveSync: "Use Path from 'effect'",
|
|
41
|
-
pathToFileURL: "Use Path from 'effect'",
|
|
42
|
-
},
|
|
43
|
-
process: {
|
|
44
|
-
env: "Use Config from 'effect'",
|
|
45
|
-
exit: "Use Effect.fail or Effect.die",
|
|
46
|
-
cwd: "Use Path from 'effect'",
|
|
47
|
-
argv: "Use Args/Command from 'effect/unstable/cli'",
|
|
48
|
-
stdin: "Use Terminal from 'effect'",
|
|
49
|
-
stdout: "Use Terminal from 'effect'",
|
|
50
|
-
stderr: "Use Terminal from 'effect'",
|
|
51
|
-
chdir: "Use Path from 'effect'",
|
|
52
|
-
kill: "Use Command from 'effect'",
|
|
53
|
-
on: "Use Effect.async or Stream.async",
|
|
54
|
-
platform: "Use PlatformInfo from 'effect'",
|
|
55
|
-
arch: "Use PlatformInfo from 'effect'",
|
|
56
|
-
execPath: "Use Command from 'effect'",
|
|
57
|
-
execArgv: "Use Args/Command from 'effect/unstable/cli'",
|
|
58
|
-
},
|
|
59
|
-
Deno: {
|
|
60
|
-
readFile: "Use FileSystem from 'effect'",
|
|
61
|
-
writeFile: "Use FileSystem from 'effect'",
|
|
62
|
-
readTextFile: "Use FileSystem from 'effect'",
|
|
63
|
-
writeTextFile: "Use FileSystem from 'effect'",
|
|
64
|
-
open: "Use FileSystem from 'effect'",
|
|
65
|
-
remove: "Use FileSystem from 'effect'",
|
|
66
|
-
mkdir: "Use FileSystem from 'effect'",
|
|
67
|
-
readDir: "Use FileSystem from 'effect'",
|
|
68
|
-
stat: "Use FileSystem from 'effect'",
|
|
69
|
-
cwd: "Use Path from 'effect'",
|
|
70
|
-
env: "Use Config from 'effect'",
|
|
71
|
-
exit: "Use Effect.fail or Effect.die",
|
|
72
|
-
serve: "Use HttpServer from 'effect/unstable/http'",
|
|
73
|
-
run: "Use Command from 'effect'",
|
|
74
|
-
args: "Use Args/Command from 'effect/unstable/cli'",
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getHint(obj: string, prop: string): string {
|
|
79
|
-
const hint = alternatives[obj]?.[prop]
|
|
80
|
-
if (hint) return `. ${hint}`
|
|
81
|
-
return ". Wrap in a service for testability"
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export const noPlatformGlobals = Rule.define({
|
|
85
|
-
name: "no-platform-globals",
|
|
86
|
-
meta: Rule.meta({
|
|
87
|
-
type: "suggestion",
|
|
88
|
-
description:
|
|
89
|
-
"Avoid platform globals (Bun.*, process.*, Deno.*) inside Effect context. Use Effect platform services.",
|
|
90
|
-
}),
|
|
91
|
-
create: function* () {
|
|
92
|
-
const ctx = yield* RuleContext
|
|
93
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
94
|
-
|
|
95
|
-
return Visitor.merge(tracker, {
|
|
96
|
-
MemberExpression: (node) =>
|
|
97
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
98
|
-
if (d <= 0) return Effect.void
|
|
99
|
-
|
|
100
|
-
const names = Option.getOrUndefined(
|
|
101
|
-
AST.memberNames(node as ESTree.MemberExpression),
|
|
102
|
-
)
|
|
103
|
-
if (names === undefined) return Effect.void
|
|
104
|
-
|
|
105
|
-
const [obj, prop] = names
|
|
106
|
-
if (!platformObjects.has(obj)) return Effect.void
|
|
107
|
-
|
|
108
|
-
return ctx.report(
|
|
109
|
-
Diagnostic.make({
|
|
110
|
-
node,
|
|
111
|
-
message: `Avoid ${obj}.${prop} inside Effect context${getHint(obj, prop)}.`,
|
|
112
|
-
}),
|
|
113
|
-
)
|
|
114
|
-
}),
|
|
115
|
-
})
|
|
116
|
-
},
|
|
117
|
-
})
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `process.env` inside Effect.gen/fn context.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect Config service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/processEnvInEffect
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
import * as Ref from "effect/Ref"
|
|
14
|
-
|
|
15
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
16
|
-
|
|
17
|
-
export const noProcessEnvInEffect = Rule.define({
|
|
18
|
-
name: "no-process-env-in-effect",
|
|
19
|
-
meta: Rule.meta({
|
|
20
|
-
type: "suggestion",
|
|
21
|
-
description: "Avoid process.env inside Effect.gen/fn. Use Config service.",
|
|
22
|
-
}),
|
|
23
|
-
create: function* () {
|
|
24
|
-
const ctx = yield* RuleContext
|
|
25
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
26
|
-
|
|
27
|
-
return Visitor.merge(tracker, {
|
|
28
|
-
MemberExpression: (node) =>
|
|
29
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
30
|
-
if (d > 0 && Option.isSome(AST.matchMember(node as ESTree.MemberExpression, "process", "env"))) {
|
|
31
|
-
return ctx.report(
|
|
32
|
-
Diagnostic.make({ node, message: "Avoid process.env inside Effect context. Use Config service." }),
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
return Effect.void
|
|
36
|
-
}),
|
|
37
|
-
})
|
|
38
|
-
},
|
|
39
|
-
})
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `process.env` access.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect Config service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/processEnv
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noProcessEnv = Rule.banMember("process", "env", {
|
|
11
|
-
message: "Avoid process.env. Use Effect Config service.",
|
|
12
|
-
meta: { type: "suggestion" },
|
|
13
|
-
})
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Math.random()` inside Effect.gen/fn context.
|
|
3
|
-
*
|
|
4
|
-
* Use Random service instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalRandomInEffect
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
import * as Ref from "effect/Ref"
|
|
14
|
-
|
|
15
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
16
|
-
|
|
17
|
-
export const noRandomInEffect = Rule.define({
|
|
18
|
-
name: "no-random-in-effect",
|
|
19
|
-
meta: Rule.meta({
|
|
20
|
-
type: "suggestion",
|
|
21
|
-
description: "Avoid Math.random() inside Effect.gen/fn. Use Random service.",
|
|
22
|
-
}),
|
|
23
|
-
create: function* () {
|
|
24
|
-
const ctx = yield* RuleContext
|
|
25
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
26
|
-
|
|
27
|
-
return Visitor.merge(tracker, {
|
|
28
|
-
MemberExpression: (node) =>
|
|
29
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
30
|
-
if (d > 0 && Option.isSome(AST.matchMember(node as ESTree.MemberExpression, "Math", "random"))) {
|
|
31
|
-
return ctx.report(
|
|
32
|
-
Diagnostic.make({ node, message: "Avoid Math.random() inside Effect context. Use Random service." }),
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
return Effect.void
|
|
36
|
-
}),
|
|
37
|
-
})
|
|
38
|
-
},
|
|
39
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `return null` statements.
|
|
3
|
-
*
|
|
4
|
-
* Use Option.none for absence or Effect.fail for errors.
|
|
5
|
-
*
|
|
6
|
-
* Source: biome-effect-linting-rules/no-return-null
|
|
7
|
-
*/
|
|
8
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import * as Effect from "effect/Effect"
|
|
11
|
-
import * as Option from "effect/Option"
|
|
12
|
-
import { pipe } from "effect/Function"
|
|
13
|
-
|
|
14
|
-
export const noReturnNull = Rule.define({
|
|
15
|
-
name: "no-return-null",
|
|
16
|
-
meta: Rule.meta({
|
|
17
|
-
type: "suggestion",
|
|
18
|
-
description:
|
|
19
|
-
"Avoid returning null. Use Option.none for absence or Effect.fail for errors.",
|
|
20
|
-
}),
|
|
21
|
-
create: function* () {
|
|
22
|
-
const ctx = yield* RuleContext
|
|
23
|
-
return {
|
|
24
|
-
ReturnStatement: (node) => {
|
|
25
|
-
if (
|
|
26
|
-
node.type === "ReturnStatement" &&
|
|
27
|
-
"argument" in node &&
|
|
28
|
-
node.argument !== null &&
|
|
29
|
-
node.argument !== undefined &&
|
|
30
|
-
"type" in node.argument &&
|
|
31
|
-
node.argument.type === "Literal" &&
|
|
32
|
-
"value" in node.argument &&
|
|
33
|
-
node.argument.value === null
|
|
34
|
-
) {
|
|
35
|
-
return ctx.report(
|
|
36
|
-
Diagnostic.make({
|
|
37
|
-
node,
|
|
38
|
-
message:
|
|
39
|
-
"Avoid returning null. Use Option.none for absence.",
|
|
40
|
-
}),
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
return Effect.void
|
|
44
|
-
},
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
})
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `Effect.runSync`, `Effect.runPromise`, `Effect.runFork`, `Effect.runCallback`.
|
|
3
|
-
*
|
|
4
|
-
* Inside Effect context, use Runtime or Effect.services pattern.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/runEffectInsideEffect
|
|
7
|
-
*/
|
|
8
|
-
import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
|
|
10
|
-
export const noRunInEffect = Rule.banMember(
|
|
11
|
-
"Effect",
|
|
12
|
-
["runSync", "runPromise", "runFork", "runCallback"],
|
|
13
|
-
{
|
|
14
|
-
message:
|
|
15
|
-
"Avoid Effect.run* calls. Use Effect.services (v4) or Runtime pattern instead.",
|
|
16
|
-
meta: { type: "problem" },
|
|
17
|
-
},
|
|
18
|
-
)
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `throw` statements inside Effect.gen/fn generators.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect.fail with tagged errors instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: effect convention — errors should be in the type system
|
|
7
|
-
*/
|
|
8
|
-
import { Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import * as Effect from "effect/Effect"
|
|
11
|
-
import * as Ref from "effect/Ref"
|
|
12
|
-
|
|
13
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
14
|
-
|
|
15
|
-
export const noThrowInEffectGen = Rule.define({
|
|
16
|
-
name: "no-throw-in-effect-gen",
|
|
17
|
-
meta: Rule.meta({
|
|
18
|
-
type: "problem",
|
|
19
|
-
description:
|
|
20
|
-
"Avoid throw inside Effect.gen/fn. Use Effect.fail with tagged errors.",
|
|
21
|
-
}),
|
|
22
|
-
create: function* () {
|
|
23
|
-
const ctx = yield* RuleContext
|
|
24
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
25
|
-
|
|
26
|
-
return Visitor.merge(tracker, {
|
|
27
|
-
ThrowStatement: (node) =>
|
|
28
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
29
|
-
if (d > 0) {
|
|
30
|
-
return ctx.report(
|
|
31
|
-
Diagnostic.make({
|
|
32
|
-
node,
|
|
33
|
-
message:
|
|
34
|
-
"Avoid throw inside Effect.gen/fn. Use Effect.fail with tagged errors.",
|
|
35
|
-
}),
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
return Effect.void
|
|
39
|
-
}),
|
|
40
|
-
})
|
|
41
|
-
},
|
|
42
|
-
})
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `setTimeout`/`setInterval` inside Effect.gen/fn context.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect.sleep or Schedule instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/globalTimersInEffect
|
|
7
|
-
*/
|
|
8
|
-
import type { ESTree } from "@oxlint/plugins"
|
|
9
|
-
import { AST, Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
11
|
-
import * as Effect from "effect/Effect"
|
|
12
|
-
import * as Option from "effect/Option"
|
|
13
|
-
import * as Ref from "effect/Ref"
|
|
14
|
-
|
|
15
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
16
|
-
|
|
17
|
-
const timerNames = ["setTimeout", "setInterval"]
|
|
18
|
-
|
|
19
|
-
export const noTimersInEffect = Rule.define({
|
|
20
|
-
name: "no-timers-in-effect",
|
|
21
|
-
meta: Rule.meta({
|
|
22
|
-
type: "suggestion",
|
|
23
|
-
description: "Avoid setTimeout/setInterval inside Effect.gen/fn. Use Effect.sleep or Schedule.",
|
|
24
|
-
}),
|
|
25
|
-
create: function* () {
|
|
26
|
-
const ctx = yield* RuleContext
|
|
27
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
28
|
-
|
|
29
|
-
return Visitor.merge(tracker, {
|
|
30
|
-
CallExpression: (node) =>
|
|
31
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
32
|
-
if (d > 0) {
|
|
33
|
-
const name = Option.getOrUndefined(AST.calleeName(node as ESTree.CallExpression))
|
|
34
|
-
if (name !== undefined && timerNames.includes(name)) {
|
|
35
|
-
return ctx.report(
|
|
36
|
-
Diagnostic.make({
|
|
37
|
-
node,
|
|
38
|
-
message: "Avoid setTimeout/setInterval inside Effect context. Use Effect.sleep or Schedule.",
|
|
39
|
-
}),
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return Effect.void
|
|
44
|
-
}),
|
|
45
|
-
})
|
|
46
|
-
},
|
|
47
|
-
})
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ban `try/catch` inside Effect.gen/fn generators.
|
|
3
|
-
*
|
|
4
|
-
* Use Effect.try, Effect.tryPromise, or Effect.catch* instead.
|
|
5
|
-
*
|
|
6
|
-
* Source: language-service/tryCatchInEffectGen
|
|
7
|
-
*/
|
|
8
|
-
import { Diagnostic, Rule, Visitor } from "../vendor/effect-oxlint/index.js"
|
|
9
|
-
import { RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
10
|
-
import * as Effect from "effect/Effect"
|
|
11
|
-
import * as Ref from "effect/Ref"
|
|
12
|
-
|
|
13
|
-
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
14
|
-
|
|
15
|
-
export const noTryCatchInEffectGen = Rule.define({
|
|
16
|
-
name: "no-try-catch-in-effect-gen",
|
|
17
|
-
meta: Rule.meta({
|
|
18
|
-
type: "suggestion",
|
|
19
|
-
description:
|
|
20
|
-
"Avoid try/catch inside Effect.gen/fn. Use Effect.try or Effect.tryPromise.",
|
|
21
|
-
}),
|
|
22
|
-
create: function* () {
|
|
23
|
-
const ctx = yield* RuleContext
|
|
24
|
-
const [depth, tracker] = yield* makeEffectContextTracker
|
|
25
|
-
|
|
26
|
-
return Visitor.merge(tracker, {
|
|
27
|
-
TryStatement: (node) =>
|
|
28
|
-
Effect.flatMap(Ref.get(depth), (d) => {
|
|
29
|
-
if (d > 0) {
|
|
30
|
-
return ctx.report(
|
|
31
|
-
Diagnostic.make({
|
|
32
|
-
node,
|
|
33
|
-
message:
|
|
34
|
-
"Avoid try/catch inside Effect.gen/fn. Use Effect.try, Effect.tryPromise, or Effect.catch*.",
|
|
35
|
-
}),
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
return Effect.void
|
|
39
|
-
}),
|
|
40
|
-
})
|
|
41
|
-
},
|
|
42
|
-
})
|