oxlint-plugin-effect 0.0.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/.effect-lsp.json +60 -0
- package/.oxlintrc.json +33 -0
- package/package.json +51 -0
- package/scripts/add-rule.ts +87 -0
- package/scripts/codegen.ts +108 -0
- package/src/index.ts +9 -0
- package/src/plugin.ts +16 -0
- package/src/presets/core.ts +36 -0
- package/src/presets/effect-native.ts +41 -0
- package/src/presets/full.ts +84 -0
- package/src/presets/index.ts +3 -0
- package/src/rules/_effect-context.ts +28 -0
- package/src/rules/index.ts +83 -0
- package/src/rules/no-arrow-ladder.ts +71 -0
- package/src/rules/no-async-function.ts +48 -0
- package/src/rules/no-console-in-effect.ts +46 -0
- package/src/rules/no-date-in-effect.ts +61 -0
- package/src/rules/no-dynamic-import.ts +13 -0
- package/src/rules/no-effect-as.ts +14 -0
- package/src/rules/no-effect-async.ts +14 -0
- package/src/rules/no-effect-bind.ts +13 -0
- package/src/rules/no-effect-do.ts +14 -0
- package/src/rules/no-effect-fn-generator.ts +59 -0
- package/src/rules/no-effect-gen-adapter.ts +51 -0
- package/src/rules/no-effect-map-void.ts +81 -0
- package/src/rules/no-effect-never.ts +14 -0
- package/src/rules/no-effect-orElse-ladder.ts +80 -0
- package/src/rules/no-effect-succeed-string.ts +53 -0
- package/src/rules/no-effect-succeed-variable.ts +51 -0
- package/src/rules/no-effect-succeed-void.ts +64 -0
- package/src/rules/no-effect-sync-console.ts +92 -0
- package/src/rules/no-effect-sync-wrapper.ts +68 -0
- package/src/rules/no-effect-type-alias.ts +72 -0
- package/src/rules/no-effect-wrapper-alias.ts +98 -0
- package/src/rules/no-extends-native-error.ts +55 -0
- package/src/rules/no-fetch-in-effect.ts +47 -0
- package/src/rules/no-flatmap-ladder.ts +79 -0
- package/src/rules/no-fromnullable-coalesce.ts +63 -0
- package/src/rules/no-global-console.ts +17 -0
- package/src/rules/no-global-date.ts +20 -0
- package/src/rules/no-global-fetch.ts +14 -0
- package/src/rules/no-global-random.ts +13 -0
- package/src/rules/no-global-timers.ts +17 -0
- package/src/rules/no-if-statement.ts +16 -0
- package/src/rules/no-iife-wrapper.ts +52 -0
- package/src/rules/no-inline-runtime-provide.ts +42 -0
- package/src/rules/no-instanceof-schema.ts +43 -0
- package/src/rules/no-json-in-effect.ts +45 -0
- package/src/rules/no-json-parse.ts +18 -0
- package/src/rules/no-manual-effect-channels.ts +59 -0
- package/src/rules/no-match-effect-branch.ts +83 -0
- package/src/rules/no-match-void-branch.ts +73 -0
- package/src/rules/no-nested-effect-call.ts +59 -0
- package/src/rules/no-nested-effect-gen.ts +44 -0
- package/src/rules/no-nested-pipe.ts +76 -0
- package/src/rules/no-new-error.ts +17 -0
- package/src/rules/no-new-promise.ts +14 -0
- package/src/rules/no-node-builtin-import.ts +70 -0
- package/src/rules/no-option-as.ts +13 -0
- package/src/rules/no-option-boolean-normalization.ts +89 -0
- package/src/rules/no-platform-globals.ts +117 -0
- package/src/rules/no-process-env-in-effect.ts +39 -0
- package/src/rules/no-process-env.ts +13 -0
- package/src/rules/no-random-in-effect.ts +39 -0
- package/src/rules/no-return-in-arrow.ts +77 -0
- package/src/rules/no-return-null.ts +47 -0
- package/src/rules/no-run-in-effect.ts +18 -0
- package/src/rules/no-runtime-run-fork.ts +14 -0
- package/src/rules/no-string-sentinel-const.ts +50 -0
- package/src/rules/no-switch-statement.ts +13 -0
- package/src/rules/no-ternary.ts +32 -0
- package/src/rules/no-throw-in-effect-gen.ts +42 -0
- package/src/rules/no-throw-statement.ts +13 -0
- package/src/rules/no-timers-in-effect.ts +47 -0
- package/src/rules/no-try-catch-in-effect-gen.ts +42 -0
- package/src/rules/no-try-catch.ts +14 -0
- package/src/rules/no-unnecessary-arrow-block.ts +52 -0
- package/src/rules/no-unnecessary-effect-gen.ts +85 -0
- package/src/rules/no-unnecessary-pipe.ts +46 -0
- package/src/vendor/effect-oxlint/AST.ts +516 -0
- package/src/vendor/effect-oxlint/Comment.ts +86 -0
- package/src/vendor/effect-oxlint/Diagnostic.ts +199 -0
- package/src/vendor/effect-oxlint/Plugin.ts +71 -0
- package/src/vendor/effect-oxlint/Rule.ts +590 -0
- package/src/vendor/effect-oxlint/RuleContext.ts +144 -0
- package/src/vendor/effect-oxlint/Scope.ts +177 -0
- package/src/vendor/effect-oxlint/SourceCode.ts +377 -0
- package/src/vendor/effect-oxlint/Testing.ts +1227 -0
- package/src/vendor/effect-oxlint/Token.ts +131 -0
- package/src/vendor/effect-oxlint/Visitor.ts +287 -0
- package/src/vendor/effect-oxlint/index.ts +88 -0
- package/tests/ban-rules.test.ts +252 -0
- package/tests/context-rules.test.ts +281 -0
- package/tests/pattern-rules.test.ts +258 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test"
|
|
2
|
+
import { Testing } from "../src/vendor/effect-oxlint/index.js"
|
|
3
|
+
import {
|
|
4
|
+
noEffectDo,
|
|
5
|
+
noEffectNever,
|
|
6
|
+
noEffectAs,
|
|
7
|
+
noEffectAsync,
|
|
8
|
+
noEffectBind,
|
|
9
|
+
noOptionAs,
|
|
10
|
+
noRuntimeRunFork,
|
|
11
|
+
noRunInEffect,
|
|
12
|
+
noGlobalFetch,
|
|
13
|
+
noGlobalConsole,
|
|
14
|
+
noGlobalDate,
|
|
15
|
+
noGlobalRandom,
|
|
16
|
+
noGlobalTimers,
|
|
17
|
+
noNewPromise,
|
|
18
|
+
noNewError,
|
|
19
|
+
noProcessEnv,
|
|
20
|
+
noNodeBuiltinImport,
|
|
21
|
+
noDynamicImport,
|
|
22
|
+
noThrowStatement,
|
|
23
|
+
noTryCatch,
|
|
24
|
+
noJsonParse,
|
|
25
|
+
} from "../src/rules/index.js"
|
|
26
|
+
|
|
27
|
+
// --- API ban rules ---
|
|
28
|
+
|
|
29
|
+
describe("noEffectDo", () => {
|
|
30
|
+
test("reports Effect.Do", () => {
|
|
31
|
+
const result = Testing.runRule(noEffectDo, "MemberExpression", Testing.memberExpr("Effect", "Do"))
|
|
32
|
+
Testing.expectDiagnostics(result, [{ message: "Avoid Effect.Do builder notation. Use flat pipe-based flow or Effect.gen." }])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test("ignores Effect.map", () => {
|
|
36
|
+
const result = Testing.runRule(noEffectDo, "MemberExpression", Testing.memberExpr("Effect", "map"))
|
|
37
|
+
Testing.expectNoDiagnostics(result)
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("noEffectNever", () => {
|
|
42
|
+
test("reports Effect.never", () => {
|
|
43
|
+
const result = Testing.runRule(noEffectNever, "MemberExpression", Testing.memberExpr("Effect", "never"))
|
|
44
|
+
Testing.expectDiagnostics(result, [{ message: "Avoid Effect.never. Use Stream or explicit acquire/release lifecycles." }])
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe("noEffectAs", () => {
|
|
49
|
+
test("reports Effect.as", () => {
|
|
50
|
+
const result = Testing.runRule(noEffectAs, "MemberExpression", Testing.memberExpr("Effect", "as"))
|
|
51
|
+
Testing.expectDiagnostics(result, [{ message: "Avoid Effect.as. Use Effect.map for value mapping or Effect.asVoid." }])
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe("noEffectAsync", () => {
|
|
56
|
+
test("reports Effect.async", () => {
|
|
57
|
+
const result = Testing.runRule(noEffectAsync, "MemberExpression", Testing.memberExpr("Effect", "async"))
|
|
58
|
+
expect(result.length).toBe(1)
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe("noEffectBind", () => {
|
|
63
|
+
test("reports Effect.bind", () => {
|
|
64
|
+
const result = Testing.runRule(noEffectBind, "MemberExpression", Testing.memberExpr("Effect", "bind"))
|
|
65
|
+
expect(result.length).toBe(1)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe("noOptionAs", () => {
|
|
70
|
+
test("reports Option.as", () => {
|
|
71
|
+
const result = Testing.runRule(noOptionAs, "MemberExpression", Testing.memberExpr("Option", "as"))
|
|
72
|
+
expect(result.length).toBe(1)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe("noRuntimeRunFork", () => {
|
|
77
|
+
test("reports Runtime.runFork", () => {
|
|
78
|
+
const result = Testing.runRule(noRuntimeRunFork, "MemberExpression", Testing.memberExpr("Runtime", "runFork"))
|
|
79
|
+
expect(result.length).toBe(1)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe("noRunInEffect", () => {
|
|
84
|
+
test("reports Effect.runSync", () => {
|
|
85
|
+
const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "runSync"))
|
|
86
|
+
expect(result.length).toBe(1)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test("reports Effect.runPromise", () => {
|
|
90
|
+
const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "runPromise"))
|
|
91
|
+
expect(result.length).toBe(1)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("ignores Effect.gen", () => {
|
|
95
|
+
const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "gen"))
|
|
96
|
+
Testing.expectNoDiagnostics(result)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// --- Global ban rules ---
|
|
101
|
+
|
|
102
|
+
describe("noGlobalFetch", () => {
|
|
103
|
+
test("reports fetch()", () => {
|
|
104
|
+
const result = Testing.runRule(noGlobalFetch, "CallExpression", Testing.callExpr("fetch"))
|
|
105
|
+
expect(result.length).toBe(1)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test("ignores other calls", () => {
|
|
109
|
+
const result = Testing.runRule(noGlobalFetch, "CallExpression", Testing.callExpr("getData"))
|
|
110
|
+
Testing.expectNoDiagnostics(result)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe("noGlobalConsole", () => {
|
|
115
|
+
test("reports console.log", () => {
|
|
116
|
+
const result = Testing.runRule(noGlobalConsole, "MemberExpression", Testing.memberExpr("console", "log"))
|
|
117
|
+
expect(result.length).toBe(1)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test("reports console.error", () => {
|
|
121
|
+
const result = Testing.runRule(noGlobalConsole, "MemberExpression", Testing.memberExpr("console", "error"))
|
|
122
|
+
expect(result.length).toBe(1)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test("ignores logger.log", () => {
|
|
126
|
+
const result = Testing.runRule(noGlobalConsole, "MemberExpression", Testing.memberExpr("logger", "log"))
|
|
127
|
+
Testing.expectNoDiagnostics(result)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe("noGlobalDate", () => {
|
|
132
|
+
test("reports Date.now", () => {
|
|
133
|
+
const result = Testing.runRule(noGlobalDate, "MemberExpression", Testing.memberExpr("Date", "now"))
|
|
134
|
+
expect(result.length).toBe(1)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test("reports new Date()", () => {
|
|
138
|
+
const result = Testing.runRule(noGlobalDate, "NewExpression", Testing.newExpr("Date"))
|
|
139
|
+
expect(result.length).toBe(1)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe("noGlobalRandom", () => {
|
|
144
|
+
test("reports Math.random", () => {
|
|
145
|
+
const result = Testing.runRule(noGlobalRandom, "MemberExpression", Testing.memberExpr("Math", "random"))
|
|
146
|
+
expect(result.length).toBe(1)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe("noGlobalTimers", () => {
|
|
151
|
+
test("reports setTimeout", () => {
|
|
152
|
+
const result = Testing.runRule(noGlobalTimers, "CallExpression", Testing.callExpr("setTimeout"))
|
|
153
|
+
expect(result.length).toBe(1)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test("reports setInterval", () => {
|
|
157
|
+
const result = Testing.runRule(noGlobalTimers, "CallExpression", Testing.callExpr("setInterval"))
|
|
158
|
+
expect(result.length).toBe(1)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
describe("noNewPromise", () => {
|
|
163
|
+
test("reports new Promise()", () => {
|
|
164
|
+
const result = Testing.runRule(noNewPromise, "NewExpression", Testing.newExpr("Promise"))
|
|
165
|
+
expect(result.length).toBe(1)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe("noNewError", () => {
|
|
170
|
+
test("reports new Error()", () => {
|
|
171
|
+
const result = Testing.runRule(noNewError, "NewExpression", Testing.newExpr("Error"))
|
|
172
|
+
expect(result.length).toBe(1)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test("reports new TypeError()", () => {
|
|
176
|
+
const result = Testing.runRule(noNewError, "NewExpression", Testing.newExpr("TypeError"))
|
|
177
|
+
expect(result.length).toBe(1)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("ignores new MyError()", () => {
|
|
181
|
+
const result = Testing.runRule(noNewError, "NewExpression", Testing.newExpr("MyError"))
|
|
182
|
+
Testing.expectNoDiagnostics(result)
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
describe("noProcessEnv", () => {
|
|
187
|
+
test("reports process.env", () => {
|
|
188
|
+
const result = Testing.runRule(noProcessEnv, "MemberExpression", Testing.memberExpr("process", "env"))
|
|
189
|
+
expect(result.length).toBe(1)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// --- Import ban rules ---
|
|
194
|
+
|
|
195
|
+
describe("noNodeBuiltinImport", () => {
|
|
196
|
+
test("reports import from 'fs'", () => {
|
|
197
|
+
const result = Testing.runRule(noNodeBuiltinImport, "ImportDeclaration", Testing.importDecl("fs"))
|
|
198
|
+
expect(result.length).toBe(1)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test("reports import from 'node:fs'", () => {
|
|
202
|
+
const result = Testing.runRule(noNodeBuiltinImport, "ImportDeclaration", Testing.importDecl("node:fs"))
|
|
203
|
+
expect(result.length).toBe(1)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test("reports import from 'child_process'", () => {
|
|
207
|
+
const result = Testing.runRule(noNodeBuiltinImport, "ImportDeclaration", Testing.importDecl("child_process"))
|
|
208
|
+
expect(result.length).toBe(1)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test("ignores import from 'effect'", () => {
|
|
212
|
+
const result = Testing.runRule(noNodeBuiltinImport, "ImportDeclaration", Testing.importDecl("effect"))
|
|
213
|
+
Testing.expectNoDiagnostics(result)
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// --- Statement ban rules ---
|
|
218
|
+
|
|
219
|
+
describe("noThrowStatement", () => {
|
|
220
|
+
test("reports throw", () => {
|
|
221
|
+
const result = Testing.runRule(noThrowStatement, "ThrowStatement", Testing.throwStmt())
|
|
222
|
+
expect(result.length).toBe(1)
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
describe("noTryCatch", () => {
|
|
227
|
+
test("reports try/catch", () => {
|
|
228
|
+
const result = Testing.runRule(noTryCatch, "TryStatement", Testing.tryStmt())
|
|
229
|
+
expect(result.length).toBe(1)
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
describe("noDynamicImport", () => {
|
|
234
|
+
test("reports dynamic import()", () => {
|
|
235
|
+
const result = Testing.runRule(noDynamicImport, "ImportExpression", { type: "ImportExpression" } as never)
|
|
236
|
+
expect(result.length).toBe(1)
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
// --- JSON ---
|
|
241
|
+
|
|
242
|
+
describe("noJsonParse", () => {
|
|
243
|
+
test("reports JSON.parse", () => {
|
|
244
|
+
const result = Testing.runRule(noJsonParse, "MemberExpression", Testing.memberExpr("JSON", "parse"))
|
|
245
|
+
expect(result.length).toBe(1)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test("reports JSON.stringify", () => {
|
|
249
|
+
const result = Testing.runRule(noJsonParse, "MemberExpression", Testing.memberExpr("JSON", "stringify"))
|
|
250
|
+
expect(result.length).toBe(1)
|
|
251
|
+
})
|
|
252
|
+
})
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test"
|
|
2
|
+
import { Testing } from "../src/vendor/effect-oxlint/index.js"
|
|
3
|
+
import {
|
|
4
|
+
noPlatformGlobals,
|
|
5
|
+
noThrowInEffectGen,
|
|
6
|
+
noTryCatchInEffectGen,
|
|
7
|
+
noConsoleInEffect,
|
|
8
|
+
noFetchInEffect,
|
|
9
|
+
noDateInEffect,
|
|
10
|
+
noRandomInEffect,
|
|
11
|
+
noTimersInEffect,
|
|
12
|
+
noJsonInEffect,
|
|
13
|
+
noProcessEnvInEffect,
|
|
14
|
+
} from "../src/rules/index.js"
|
|
15
|
+
|
|
16
|
+
// Helper: simulate entering and exiting Effect.gen context
|
|
17
|
+
const effectGenNode = Testing.callOfMember("Effect", "gen", [])
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* For context-aware rules, we need to:
|
|
21
|
+
* 1. Enter Effect.gen (CallExpression event)
|
|
22
|
+
* 2. Fire the target event (ThrowStatement, MemberExpression, etc.)
|
|
23
|
+
* 3. Exit Effect.gen (CallExpression:exit event)
|
|
24
|
+
*
|
|
25
|
+
* Using runRuleMulti to simulate the sequence.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
describe("noThrowInEffectGen", () => {
|
|
29
|
+
test("reports throw inside Effect.gen", () => {
|
|
30
|
+
const result = Testing.runRuleMulti(noThrowInEffectGen, [
|
|
31
|
+
["CallExpression", effectGenNode],
|
|
32
|
+
["ThrowStatement", Testing.throwStmt()],
|
|
33
|
+
["CallExpression:exit", effectGenNode],
|
|
34
|
+
])
|
|
35
|
+
expect(result.length).toBe(1)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("ignores throw outside Effect.gen", () => {
|
|
39
|
+
const result = Testing.runRuleMulti(noThrowInEffectGen, [
|
|
40
|
+
["ThrowStatement", Testing.throwStmt()],
|
|
41
|
+
])
|
|
42
|
+
Testing.expectNoDiagnostics(result)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
describe("noTryCatchInEffectGen", () => {
|
|
47
|
+
test("reports try/catch inside Effect.gen", () => {
|
|
48
|
+
const result = Testing.runRuleMulti(noTryCatchInEffectGen, [
|
|
49
|
+
["CallExpression", effectGenNode],
|
|
50
|
+
["TryStatement", Testing.tryStmt()],
|
|
51
|
+
["CallExpression:exit", effectGenNode],
|
|
52
|
+
])
|
|
53
|
+
expect(result.length).toBe(1)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test("ignores try/catch outside Effect.gen", () => {
|
|
57
|
+
const result = Testing.runRuleMulti(noTryCatchInEffectGen, [
|
|
58
|
+
["TryStatement", Testing.tryStmt()],
|
|
59
|
+
])
|
|
60
|
+
Testing.expectNoDiagnostics(result)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe("noConsoleInEffect", () => {
|
|
65
|
+
test("reports console.log inside Effect.gen", () => {
|
|
66
|
+
const result = Testing.runRuleMulti(noConsoleInEffect, [
|
|
67
|
+
["CallExpression", effectGenNode],
|
|
68
|
+
["MemberExpression", Testing.memberExpr("console", "log")],
|
|
69
|
+
["CallExpression:exit", effectGenNode],
|
|
70
|
+
])
|
|
71
|
+
expect(result.length).toBe(1)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("ignores console.log outside Effect.gen", () => {
|
|
75
|
+
const result = Testing.runRuleMulti(noConsoleInEffect, [
|
|
76
|
+
["MemberExpression", Testing.memberExpr("console", "log")],
|
|
77
|
+
])
|
|
78
|
+
Testing.expectNoDiagnostics(result)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe("noFetchInEffect", () => {
|
|
83
|
+
test("reports fetch() inside Effect.gen", () => {
|
|
84
|
+
const result = Testing.runRuleMulti(noFetchInEffect, [
|
|
85
|
+
["CallExpression", effectGenNode],
|
|
86
|
+
["CallExpression", Testing.callExpr("fetch")],
|
|
87
|
+
["CallExpression:exit", effectGenNode],
|
|
88
|
+
])
|
|
89
|
+
expect(result.length).toBe(1)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("ignores fetch() outside Effect.gen", () => {
|
|
93
|
+
const result = Testing.runRuleMulti(noFetchInEffect, [
|
|
94
|
+
["CallExpression", Testing.callExpr("fetch")],
|
|
95
|
+
])
|
|
96
|
+
Testing.expectNoDiagnostics(result)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe("noDateInEffect", () => {
|
|
101
|
+
test("reports Date.now inside Effect.gen", () => {
|
|
102
|
+
const result = Testing.runRuleMulti(noDateInEffect, [
|
|
103
|
+
["CallExpression", effectGenNode],
|
|
104
|
+
["MemberExpression", Testing.memberExpr("Date", "now")],
|
|
105
|
+
["CallExpression:exit", effectGenNode],
|
|
106
|
+
])
|
|
107
|
+
expect(result.length).toBe(1)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test("reports new Date() inside Effect.gen", () => {
|
|
111
|
+
const result = Testing.runRuleMulti(noDateInEffect, [
|
|
112
|
+
["CallExpression", effectGenNode],
|
|
113
|
+
["NewExpression", Testing.newExpr("Date")],
|
|
114
|
+
["CallExpression:exit", effectGenNode],
|
|
115
|
+
])
|
|
116
|
+
expect(result.length).toBe(1)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test("ignores Date.now outside Effect.gen", () => {
|
|
120
|
+
const result = Testing.runRuleMulti(noDateInEffect, [
|
|
121
|
+
["MemberExpression", Testing.memberExpr("Date", "now")],
|
|
122
|
+
])
|
|
123
|
+
Testing.expectNoDiagnostics(result)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
describe("noRandomInEffect", () => {
|
|
128
|
+
test("reports Math.random inside Effect.gen", () => {
|
|
129
|
+
const result = Testing.runRuleMulti(noRandomInEffect, [
|
|
130
|
+
["CallExpression", effectGenNode],
|
|
131
|
+
["MemberExpression", Testing.memberExpr("Math", "random")],
|
|
132
|
+
["CallExpression:exit", effectGenNode],
|
|
133
|
+
])
|
|
134
|
+
expect(result.length).toBe(1)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test("ignores Math.random outside Effect.gen", () => {
|
|
138
|
+
const result = Testing.runRuleMulti(noRandomInEffect, [
|
|
139
|
+
["MemberExpression", Testing.memberExpr("Math", "random")],
|
|
140
|
+
])
|
|
141
|
+
Testing.expectNoDiagnostics(result)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
describe("noTimersInEffect", () => {
|
|
146
|
+
test("reports setTimeout inside Effect.gen", () => {
|
|
147
|
+
const result = Testing.runRuleMulti(noTimersInEffect, [
|
|
148
|
+
["CallExpression", effectGenNode],
|
|
149
|
+
["CallExpression", Testing.callExpr("setTimeout")],
|
|
150
|
+
["CallExpression:exit", effectGenNode],
|
|
151
|
+
])
|
|
152
|
+
expect(result.length).toBe(1)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test("ignores setTimeout outside Effect.gen", () => {
|
|
156
|
+
const result = Testing.runRuleMulti(noTimersInEffect, [
|
|
157
|
+
["CallExpression", Testing.callExpr("setTimeout")],
|
|
158
|
+
])
|
|
159
|
+
Testing.expectNoDiagnostics(result)
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
describe("noJsonInEffect", () => {
|
|
164
|
+
test("reports JSON.parse inside Effect.gen", () => {
|
|
165
|
+
const result = Testing.runRuleMulti(noJsonInEffect, [
|
|
166
|
+
["CallExpression", effectGenNode],
|
|
167
|
+
["MemberExpression", Testing.memberExpr("JSON", "parse")],
|
|
168
|
+
["CallExpression:exit", effectGenNode],
|
|
169
|
+
])
|
|
170
|
+
expect(result.length).toBe(1)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test("ignores JSON.parse outside Effect.gen", () => {
|
|
174
|
+
const result = Testing.runRuleMulti(noJsonInEffect, [
|
|
175
|
+
["MemberExpression", Testing.memberExpr("JSON", "parse")],
|
|
176
|
+
])
|
|
177
|
+
Testing.expectNoDiagnostics(result)
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
describe("noProcessEnvInEffect", () => {
|
|
182
|
+
test("reports process.env inside Effect.gen", () => {
|
|
183
|
+
const result = Testing.runRuleMulti(noProcessEnvInEffect, [
|
|
184
|
+
["CallExpression", effectGenNode],
|
|
185
|
+
["MemberExpression", Testing.memberExpr("process", "env")],
|
|
186
|
+
["CallExpression:exit", effectGenNode],
|
|
187
|
+
])
|
|
188
|
+
expect(result.length).toBe(1)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test("ignores process.env outside Effect.gen", () => {
|
|
192
|
+
const result = Testing.runRuleMulti(noProcessEnvInEffect, [
|
|
193
|
+
["MemberExpression", Testing.memberExpr("process", "env")],
|
|
194
|
+
])
|
|
195
|
+
Testing.expectNoDiagnostics(result)
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
describe("noPlatformGlobals", () => {
|
|
200
|
+
test("reports Bun.file with FileSystem hint", () => {
|
|
201
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
202
|
+
["CallExpression", effectGenNode],
|
|
203
|
+
["MemberExpression", Testing.memberExpr("Bun", "file")],
|
|
204
|
+
["CallExpression:exit", effectGenNode],
|
|
205
|
+
])
|
|
206
|
+
Testing.expectDiagnostics(result, [{
|
|
207
|
+
message: "Avoid Bun.file inside Effect context. Use FileSystem from 'effect'.",
|
|
208
|
+
}])
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test("reports Bun.serve with HttpServer hint", () => {
|
|
212
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
213
|
+
["CallExpression", effectGenNode],
|
|
214
|
+
["MemberExpression", Testing.memberExpr("Bun", "serve")],
|
|
215
|
+
["CallExpression:exit", effectGenNode],
|
|
216
|
+
])
|
|
217
|
+
Testing.expectDiagnostics(result, [{
|
|
218
|
+
message: "Avoid Bun.serve inside Effect context. Use HttpServer from 'effect/unstable/http'.",
|
|
219
|
+
}])
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test("reports process.exit with Effect.fail hint", () => {
|
|
223
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
224
|
+
["CallExpression", effectGenNode],
|
|
225
|
+
["MemberExpression", Testing.memberExpr("process", "exit")],
|
|
226
|
+
["CallExpression:exit", effectGenNode],
|
|
227
|
+
])
|
|
228
|
+
Testing.expectDiagnostics(result, [{
|
|
229
|
+
message: "Avoid process.exit inside Effect context. Use Effect.fail or Effect.die.",
|
|
230
|
+
}])
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
test("reports process.cwd with Path hint", () => {
|
|
234
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
235
|
+
["CallExpression", effectGenNode],
|
|
236
|
+
["MemberExpression", Testing.memberExpr("process", "cwd")],
|
|
237
|
+
["CallExpression:exit", effectGenNode],
|
|
238
|
+
])
|
|
239
|
+
Testing.expectDiagnostics(result, [{
|
|
240
|
+
message: "Avoid process.cwd inside Effect context. Use Path from 'effect'.",
|
|
241
|
+
}])
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test("reports Deno.readFile with FileSystem hint", () => {
|
|
245
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
246
|
+
["CallExpression", effectGenNode],
|
|
247
|
+
["MemberExpression", Testing.memberExpr("Deno", "readFile")],
|
|
248
|
+
["CallExpression:exit", effectGenNode],
|
|
249
|
+
])
|
|
250
|
+
Testing.expectDiagnostics(result, [{
|
|
251
|
+
message: "Avoid Deno.readFile inside Effect context. Use FileSystem from 'effect'.",
|
|
252
|
+
}])
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test("reports unknown property with generic hint", () => {
|
|
256
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
257
|
+
["CallExpression", effectGenNode],
|
|
258
|
+
["MemberExpression", Testing.memberExpr("Bun", "someNewApi")],
|
|
259
|
+
["CallExpression:exit", effectGenNode],
|
|
260
|
+
])
|
|
261
|
+
Testing.expectDiagnostics(result, [{
|
|
262
|
+
message: "Avoid Bun.someNewApi inside Effect context. Wrap in a service for testability.",
|
|
263
|
+
}])
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
test("ignores Bun.file outside Effect.gen", () => {
|
|
267
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
268
|
+
["MemberExpression", Testing.memberExpr("Bun", "file")],
|
|
269
|
+
])
|
|
270
|
+
Testing.expectNoDiagnostics(result)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test("ignores non-platform globals inside Effect.gen", () => {
|
|
274
|
+
const result = Testing.runRuleMulti(noPlatformGlobals, [
|
|
275
|
+
["CallExpression", effectGenNode],
|
|
276
|
+
["MemberExpression", Testing.memberExpr("Effect", "succeed")],
|
|
277
|
+
["CallExpression:exit", effectGenNode],
|
|
278
|
+
])
|
|
279
|
+
Testing.expectNoDiagnostics(result)
|
|
280
|
+
})
|
|
281
|
+
})
|