oxlint-plugin-effect 0.1.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 +18 -0
- package/package.json +1 -1
- package/src/presets/core.ts +1 -1
- package/src/presets/full.ts +1 -1
- package/src/presets/strict.ts +1 -1
- package/src/rules/index.ts +1 -1
- package/src/rules/no-catch-all-to-map-error.ts +1 -0
- package/src/rules/no-effect-gen-adapter.ts +1 -0
- package/src/rules/no-run-in-effect-gen.ts +66 -0
- package/src/rules/no-runtime-run-fork.ts +1 -1
- package/src/vendor/effect-oxlint/Rule.ts +32 -12
- package/tests/ban-rules.test.ts +0 -17
- package/tests/context-rules.test.ts +65 -0
- package/src/rules/no-run-in-effect.ts +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# oxlint-plugin-effect
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`5ecb6fd`](https://github.com/cevr/effect-oxlint/commit/5ecb6fd543d6183d34f037c407f0a9cc0defc5e6) Thanks [@cevr](https://github.com/cevr)! - Support v3/v4 dual-version codebases and add rule-version metadata.
|
|
8
|
+
|
|
9
|
+
**`tsgolint-effect` (Go type-aware linter):**
|
|
10
|
+
|
|
11
|
+
- `IsEffectPackageSymbol` now matches paths across all mainstream package managers (npm/yarn-classic `node_modules/effect`, pnpm `.pnpm/effect@`, bun isolated `.bun/effect@`, yarn berry `.yarn/cache/effect-npm-`) and the `effect-v3` alias used by dual-version projects. Previously, v3 code imported as `effect-v3` silently bypassed every type-aware rule.
|
|
12
|
+
- `IsEffectType`, `IsLayerType`, `IsStreamType`, `IsScopeType` now check the `~effect/*` brand property on types first, falling back to the symbol-name + path heuristic. More robust across package managers and future-proof against package-name changes.
|
|
13
|
+
- Added `Rule.EffectVersion` field (`"v3" | "v4" | "both"`, default `"both"`) for version-specific rules.
|
|
14
|
+
|
|
15
|
+
**`oxlint-plugin-effect` (JS AST linter):**
|
|
16
|
+
|
|
17
|
+
- Added `meta.docs.effectVersion` on rules (`"v3" | "v4" | "both"`, default `"both"`). Preset authors can filter rules per project version.
|
|
18
|
+
- Tagged the following rules as `"v3"` (they detect v3-only APIs): `noCatchAllToMapError`, `noEffectGenAdapter`, `noRuntimeRunFork`.
|
|
19
|
+
- **Renamed `noRunInEffect` → `noRunInEffectGen`.** The old rule fired globally (outside Effect context too — a bug). The new rule is context-aware via `makeEffectContextTracker` and only fires inside `Effect.gen`/`Effect.fn`. Expanded coverage to `Runtime.run{Sync,Promise,Fork,Callback}`. Explicitly permits `Effect.run*With` (v4 explicit-services variants) and `<identifier>.run*` (ManagedRuntime-shaped calls) — both are legitimate at callback boundaries. Preset users must update `"effect/noRunInEffect"` references to `"effect/noRunInEffectGen"`.
|
|
20
|
+
|
|
3
21
|
## 0.1.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/package.json
CHANGED
package/src/presets/core.ts
CHANGED
package/src/presets/full.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const full = {
|
|
|
13
13
|
"effect/noEffectBind": "error",
|
|
14
14
|
"effect/noOptionAs": "error",
|
|
15
15
|
"effect/noRuntimeRunFork": "error",
|
|
16
|
-
"effect/
|
|
16
|
+
"effect/noRunInEffectGen": "error",
|
|
17
17
|
|
|
18
18
|
// Global bans (everywhere, not just Effect context)
|
|
19
19
|
"effect/noAsyncFunction": "warn",
|
package/src/presets/strict.ts
CHANGED
|
@@ -11,7 +11,7 @@ export const strict = {
|
|
|
11
11
|
"effect/noEffectBind": "error",
|
|
12
12
|
"effect/noOptionAs": "error",
|
|
13
13
|
"effect/noRuntimeRunFork": "error",
|
|
14
|
-
"effect/
|
|
14
|
+
"effect/noRunInEffectGen": "error",
|
|
15
15
|
|
|
16
16
|
// Style / functional
|
|
17
17
|
"effect/noReturnNull": "error",
|
package/src/rules/index.ts
CHANGED
|
@@ -11,7 +11,6 @@ export { noEffectBind } from "./no-effect-bind.js"
|
|
|
11
11
|
export { noEffectDo } from "./no-effect-do.js"
|
|
12
12
|
export { noEffectNever } from "./no-effect-never.js"
|
|
13
13
|
export { noOptionAs } from "./no-option-as.js"
|
|
14
|
-
export { noRunInEffect } from "./no-run-in-effect.js"
|
|
15
14
|
export { noRuntimeRunFork } from "./no-runtime-run-fork.js"
|
|
16
15
|
|
|
17
16
|
// --- Statement bans ---
|
|
@@ -68,5 +67,6 @@ export { noInlineProvide } from "./no-inline-provide.js"
|
|
|
68
67
|
export { noNewError } from "./no-new-error.js"
|
|
69
68
|
export { noNewPromise } from "./no-new-promise.js"
|
|
70
69
|
export { noReturnNullish } from "./no-return-nullish.js"
|
|
70
|
+
export { noRunInEffectGen } from "./no-run-in-effect-gen.js"
|
|
71
71
|
export { noThrowStatement } from "./no-throw-statement.js"
|
|
72
72
|
export { noTryCatch } from "./no-try-catch.js"
|
|
@@ -14,6 +14,7 @@ export const noEffectGenAdapter = Rule.define({
|
|
|
14
14
|
meta: Rule.meta({
|
|
15
15
|
type: "suggestion",
|
|
16
16
|
description: "Deprecated adapter parameter in Effect.gen. Use `yield*` directly.",
|
|
17
|
+
effectVersion: "v3",
|
|
17
18
|
}),
|
|
18
19
|
create: function* () {
|
|
19
20
|
const ctx = yield* RuleContext
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ban `Effect.run*` and `Runtime.run*` calls inside `Effect.gen`/`Effect.fn`.
|
|
3
|
+
*
|
|
4
|
+
* Running an effect inside a generator drops the ambient context — fibers,
|
|
5
|
+
* services, tracing, and memoized layers. Just `yield*` the child effect;
|
|
6
|
+
* for callback boundaries use `Effect.async`, for fork use `Effect.fork`.
|
|
7
|
+
*
|
|
8
|
+
* Explicitly allowed inside gen: `Effect.runForkWith`, `Effect.runPromiseWith`,
|
|
9
|
+
* `Effect.runSyncWith` (they take services explicitly — legitimate at
|
|
10
|
+
* callback boundaries) and `managedRuntime.runX` on identifier callees
|
|
11
|
+
* (can't be discriminated syntactically).
|
|
12
|
+
*
|
|
13
|
+
* Source: language-service/runEffectInsideEffect, extended for v4.
|
|
14
|
+
*/
|
|
15
|
+
import type { ESTree } from "@oxlint/plugins"
|
|
16
|
+
import { AST, Diagnostic, Rule, Visitor, RuleContext } from "../vendor/effect-oxlint/index.js"
|
|
17
|
+
import * as Effect from "effect/Effect"
|
|
18
|
+
import * as Ref from "effect/Ref"
|
|
19
|
+
|
|
20
|
+
import { makeEffectContextTracker } from "./_effect-context.js"
|
|
21
|
+
|
|
22
|
+
const effectRunMethods = ["runSync", "runPromise", "runFork", "runCallback"]
|
|
23
|
+
const runtimeRunMethods = ["runSync", "runPromise", "runFork", "runCallback"]
|
|
24
|
+
|
|
25
|
+
export const noRunInEffectGen = Rule.define({
|
|
26
|
+
name: "no-run-in-effect-gen",
|
|
27
|
+
meta: Rule.meta({
|
|
28
|
+
type: "problem",
|
|
29
|
+
description:
|
|
30
|
+
"Running effects inside Effect.gen/fn drops the ambient context. Use yield* instead.",
|
|
31
|
+
}),
|
|
32
|
+
create: function* () {
|
|
33
|
+
const ctx = yield* RuleContext
|
|
34
|
+
const [depth, tracker] = yield* makeEffectContextTracker
|
|
35
|
+
|
|
36
|
+
return Visitor.merge(tracker, {
|
|
37
|
+
CallExpression: (node) =>
|
|
38
|
+
Effect.flatMap(Ref.get(depth), (d) => {
|
|
39
|
+
if (d <= 0) return Effect.void
|
|
40
|
+
const call = node as ESTree.CallExpression
|
|
41
|
+
|
|
42
|
+
if (AST.isCallOf(call, "Effect", effectRunMethods)) {
|
|
43
|
+
return ctx.report(
|
|
44
|
+
Diagnostic.make({
|
|
45
|
+
node,
|
|
46
|
+
message:
|
|
47
|
+
"Avoid Effect.run* inside Effect.gen/fn. `yield*` the child effect, or use Effect.fork for concurrency.",
|
|
48
|
+
}),
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (AST.isCallOf(call, "Runtime", runtimeRunMethods)) {
|
|
53
|
+
return ctx.report(
|
|
54
|
+
Diagnostic.make({
|
|
55
|
+
node,
|
|
56
|
+
message:
|
|
57
|
+
"Avoid Runtime.run* inside Effect.gen/fn. The ambient context already has your services — `yield*` the child effect.",
|
|
58
|
+
}),
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return Effect.void
|
|
63
|
+
}),
|
|
64
|
+
})
|
|
65
|
+
},
|
|
66
|
+
})
|
|
@@ -10,5 +10,5 @@ import { Rule } from "../vendor/effect-oxlint/index.js"
|
|
|
10
10
|
export const noRuntimeRunFork = Rule.banMember("Runtime", "runFork", {
|
|
11
11
|
message:
|
|
12
12
|
"Avoid Runtime.runFork. Use forkScoped, Stream, or runtime-provided layers.",
|
|
13
|
-
meta: { type: "problem" },
|
|
13
|
+
meta: { type: "problem", effectVersion: "v3" },
|
|
14
14
|
})
|
|
@@ -130,6 +130,16 @@ export const define = <Options = undefined>(
|
|
|
130
130
|
// Metadata helper
|
|
131
131
|
// ---------------------------------------------------------------------------
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Which Effect version a rule applies to.
|
|
135
|
+
*
|
|
136
|
+
* Stored on `meta.docs.effectVersion` so preset authors can filter rules
|
|
137
|
+
* per project. Default `both`, meaning the rule is version-agnostic.
|
|
138
|
+
*
|
|
139
|
+
* @since 0.2.0
|
|
140
|
+
*/
|
|
141
|
+
export type EffectVersion = 'v3' | 'v4' | 'both';
|
|
142
|
+
|
|
133
143
|
/**
|
|
134
144
|
* Build `RuleMeta` with sensible defaults.
|
|
135
145
|
*
|
|
@@ -142,6 +152,7 @@ export const meta = (opts: {
|
|
|
142
152
|
readonly hasSuggestions?: boolean | undefined;
|
|
143
153
|
readonly messages?: Record<string, string> | undefined;
|
|
144
154
|
readonly docs?: RuleDocs | undefined;
|
|
155
|
+
readonly effectVersion?: EffectVersion | undefined;
|
|
145
156
|
}): RuleMeta => ({
|
|
146
157
|
type: opts.type,
|
|
147
158
|
...(opts.fixable !== undefined ? { fixable: opts.fixable } : {}),
|
|
@@ -151,6 +162,7 @@ export const meta = (opts: {
|
|
|
151
162
|
...(opts.messages !== undefined ? { messages: opts.messages } : {}),
|
|
152
163
|
docs: {
|
|
153
164
|
description: opts.description,
|
|
165
|
+
effectVersion: opts.effectVersion ?? 'both',
|
|
154
166
|
...opts.docs
|
|
155
167
|
}
|
|
156
168
|
});
|
|
@@ -172,7 +184,7 @@ export const banMember = (
|
|
|
172
184
|
opts: {
|
|
173
185
|
readonly message: string;
|
|
174
186
|
readonly meta?:
|
|
175
|
-
| { readonly type?: 'problem' | 'suggestion' }
|
|
187
|
+
| { readonly type?: 'problem' | 'suggestion'; readonly effectVersion?: EffectVersion }
|
|
176
188
|
| undefined;
|
|
177
189
|
}
|
|
178
190
|
): CreateRule =>
|
|
@@ -180,7 +192,8 @@ export const banMember = (
|
|
|
180
192
|
name: `ban-${obj}-${P.isString(prop) ? prop : Arr.join(prop, '-')}`,
|
|
181
193
|
meta: meta({
|
|
182
194
|
type: opts.meta?.type ?? 'suggestion',
|
|
183
|
-
description: opts.message
|
|
195
|
+
description: opts.message,
|
|
196
|
+
effectVersion: opts.meta?.effectVersion
|
|
184
197
|
}),
|
|
185
198
|
create: function* () {
|
|
186
199
|
const ctx = yield* RuleContext;
|
|
@@ -216,7 +229,7 @@ export const banImport = (
|
|
|
216
229
|
opts: {
|
|
217
230
|
readonly message: string;
|
|
218
231
|
readonly meta?:
|
|
219
|
-
| { readonly type?: 'problem' | 'suggestion' }
|
|
232
|
+
| { readonly type?: 'problem' | 'suggestion'; readonly effectVersion?: EffectVersion }
|
|
220
233
|
| undefined;
|
|
221
234
|
}
|
|
222
235
|
): CreateRule =>
|
|
@@ -224,7 +237,8 @@ export const banImport = (
|
|
|
224
237
|
name: 'ban-import',
|
|
225
238
|
meta: meta({
|
|
226
239
|
type: opts.meta?.type ?? 'suggestion',
|
|
227
|
-
description: opts.message
|
|
240
|
+
description: opts.message,
|
|
241
|
+
effectVersion: opts.meta?.effectVersion
|
|
228
242
|
}),
|
|
229
243
|
create: function* () {
|
|
230
244
|
const ctx = yield* RuleContext;
|
|
@@ -270,7 +284,7 @@ export const banCallOf = (
|
|
|
270
284
|
opts: {
|
|
271
285
|
readonly message: string;
|
|
272
286
|
readonly meta?:
|
|
273
|
-
| { readonly type?: 'problem' | 'suggestion' }
|
|
287
|
+
| { readonly type?: 'problem' | 'suggestion'; readonly effectVersion?: EffectVersion }
|
|
274
288
|
| undefined;
|
|
275
289
|
}
|
|
276
290
|
): CreateRule => {
|
|
@@ -279,7 +293,8 @@ export const banCallOf = (
|
|
|
279
293
|
name: `ban-call-${Arr.join(names, '-')}`,
|
|
280
294
|
meta: meta({
|
|
281
295
|
type: opts.meta?.type ?? 'suggestion',
|
|
282
|
-
description: opts.message
|
|
296
|
+
description: opts.message,
|
|
297
|
+
effectVersion: opts.meta?.effectVersion
|
|
283
298
|
}),
|
|
284
299
|
create: function* () {
|
|
285
300
|
const ctx = yield* RuleContext;
|
|
@@ -327,7 +342,7 @@ export const banNewExpr = (
|
|
|
327
342
|
opts: {
|
|
328
343
|
readonly message: string;
|
|
329
344
|
readonly meta?:
|
|
330
|
-
| { readonly type?: 'problem' | 'suggestion' }
|
|
345
|
+
| { readonly type?: 'problem' | 'suggestion'; readonly effectVersion?: EffectVersion }
|
|
331
346
|
| undefined;
|
|
332
347
|
}
|
|
333
348
|
): CreateRule => {
|
|
@@ -336,7 +351,8 @@ export const banNewExpr = (
|
|
|
336
351
|
name: `ban-new-${Arr.join(names, '-')}`,
|
|
337
352
|
meta: meta({
|
|
338
353
|
type: opts.meta?.type ?? 'suggestion',
|
|
339
|
-
description: opts.message
|
|
354
|
+
description: opts.message,
|
|
355
|
+
effectVersion: opts.meta?.effectVersion
|
|
340
356
|
}),
|
|
341
357
|
create: function* () {
|
|
342
358
|
const ctx = yield* RuleContext;
|
|
@@ -372,7 +388,7 @@ export const banStatement = (
|
|
|
372
388
|
opts: {
|
|
373
389
|
readonly message: string;
|
|
374
390
|
readonly meta?:
|
|
375
|
-
| { readonly type?: 'problem' | 'suggestion' }
|
|
391
|
+
| { readonly type?: 'problem' | 'suggestion'; readonly effectVersion?: EffectVersion }
|
|
376
392
|
| undefined;
|
|
377
393
|
}
|
|
378
394
|
): CreateRule =>
|
|
@@ -380,7 +396,8 @@ export const banStatement = (
|
|
|
380
396
|
name: `ban-${nodeType}`,
|
|
381
397
|
meta: meta({
|
|
382
398
|
type: opts.meta?.type ?? 'suggestion',
|
|
383
|
-
description: opts.message
|
|
399
|
+
description: opts.message,
|
|
400
|
+
effectVersion: opts.meta?.effectVersion
|
|
384
401
|
}),
|
|
385
402
|
create: function* () {
|
|
386
403
|
const ctx = yield* RuleContext;
|
|
@@ -427,7 +444,9 @@ export type BanSpec =
|
|
|
427
444
|
*/
|
|
428
445
|
export const banMultiple = (config: {
|
|
429
446
|
readonly name: string;
|
|
430
|
-
readonly meta?:
|
|
447
|
+
readonly meta?:
|
|
448
|
+
| { readonly type?: 'problem' | 'suggestion'; readonly effectVersion?: EffectVersion }
|
|
449
|
+
| undefined;
|
|
431
450
|
readonly specs: ReadonlyArray<BanSpec>;
|
|
432
451
|
}): CreateRule => {
|
|
433
452
|
const description = config.specs.map((s) => s.message).join(' ');
|
|
@@ -435,7 +454,8 @@ export const banMultiple = (config: {
|
|
|
435
454
|
name: config.name,
|
|
436
455
|
meta: meta({
|
|
437
456
|
type: config.meta?.type ?? 'suggestion',
|
|
438
|
-
description
|
|
457
|
+
description,
|
|
458
|
+
effectVersion: config.meta?.effectVersion
|
|
439
459
|
}),
|
|
440
460
|
create: function* () {
|
|
441
461
|
const ctx = yield* RuleContext;
|
package/tests/ban-rules.test.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
noEffectBind,
|
|
9
9
|
noOptionAs,
|
|
10
10
|
noRuntimeRunFork,
|
|
11
|
-
noRunInEffect,
|
|
12
11
|
} from "../src/rules/index.js"
|
|
13
12
|
|
|
14
13
|
// --- API ban rules ---
|
|
@@ -67,19 +66,3 @@ describe("noRuntimeRunFork", () => {
|
|
|
67
66
|
})
|
|
68
67
|
})
|
|
69
68
|
|
|
70
|
-
describe("noRunInEffect", () => {
|
|
71
|
-
test("reports Effect.runSync", () => {
|
|
72
|
-
const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "runSync"))
|
|
73
|
-
expect(result.length).toBe(1)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
test("reports Effect.runPromise", () => {
|
|
77
|
-
const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "runPromise"))
|
|
78
|
-
expect(result.length).toBe(1)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
test("ignores Effect.gen", () => {
|
|
82
|
-
const result = Testing.runRule(noRunInEffect, "MemberExpression", Testing.memberExpr("Effect", "gen"))
|
|
83
|
-
Testing.expectNoDiagnostics(result)
|
|
84
|
-
})
|
|
85
|
-
})
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
noNewPromise,
|
|
8
8
|
noNewError,
|
|
9
9
|
noReturnNullish,
|
|
10
|
+
noRunInEffectGen,
|
|
10
11
|
} from "../src/rules/index.js"
|
|
11
12
|
|
|
12
13
|
// Helper: simulate entering and exiting Effect.gen context
|
|
@@ -196,3 +197,67 @@ describe("noGlobals", () => {
|
|
|
196
197
|
Testing.expectNoDiagnostics(result)
|
|
197
198
|
})
|
|
198
199
|
})
|
|
200
|
+
|
|
201
|
+
describe("noRunInEffectGen", () => {
|
|
202
|
+
test("reports Effect.runPromise inside Effect.gen", () => {
|
|
203
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
204
|
+
["CallExpression", effectGenNode],
|
|
205
|
+
["CallExpression", Testing.callOfMember("Effect", "runPromise", [Testing.id("x")])],
|
|
206
|
+
["CallExpression:exit", effectGenNode],
|
|
207
|
+
])
|
|
208
|
+
expect(result.length).toBe(1)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test("reports Effect.runFork inside Effect.fn", () => {
|
|
212
|
+
const effectFnNode = Testing.callOfMember("Effect", "fn", [])
|
|
213
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
214
|
+
["CallExpression", effectFnNode],
|
|
215
|
+
["CallExpression", Testing.callOfMember("Effect", "runFork", [Testing.id("x")])],
|
|
216
|
+
["CallExpression:exit", effectFnNode],
|
|
217
|
+
])
|
|
218
|
+
expect(result.length).toBe(1)
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
test("reports Runtime.runFork inside Effect.gen", () => {
|
|
222
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
223
|
+
["CallExpression", effectGenNode],
|
|
224
|
+
["CallExpression", Testing.callOfMember("Runtime", "runFork", [Testing.id("x")])],
|
|
225
|
+
["CallExpression:exit", effectGenNode],
|
|
226
|
+
])
|
|
227
|
+
expect(result.length).toBe(1)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
test("ignores Effect.runForkWith inside Effect.gen (legit v4 callback boundary)", () => {
|
|
231
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
232
|
+
["CallExpression", effectGenNode],
|
|
233
|
+
["CallExpression", Testing.callOfMember("Effect", "runForkWith", [Testing.id("services")])],
|
|
234
|
+
["CallExpression:exit", effectGenNode],
|
|
235
|
+
])
|
|
236
|
+
Testing.expectNoDiagnostics(result)
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
test("ignores managedRuntime.runFork on identifier callee", () => {
|
|
240
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
241
|
+
["CallExpression", effectGenNode],
|
|
242
|
+
["CallExpression", Testing.callOfMember("managedRuntime", "runFork", [Testing.id("x")])],
|
|
243
|
+
["CallExpression:exit", effectGenNode],
|
|
244
|
+
])
|
|
245
|
+
Testing.expectNoDiagnostics(result)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test("ignores Effect.runPromise outside Effect.gen", () => {
|
|
249
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
250
|
+
["CallExpression", Testing.callOfMember("Effect", "runPromise", [Testing.id("x")])],
|
|
251
|
+
])
|
|
252
|
+
Testing.expectNoDiagnostics(result)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test("ignores yield* child effect inside Effect.gen", () => {
|
|
256
|
+
const result = Testing.runRuleMulti(noRunInEffectGen, [
|
|
257
|
+
["CallExpression", effectGenNode],
|
|
258
|
+
["CallExpression", Testing.callOfMember("Effect", "succeed", [Testing.id("x")])],
|
|
259
|
+
["CallExpression:exit", effectGenNode],
|
|
260
|
+
])
|
|
261
|
+
Testing.expectNoDiagnostics(result)
|
|
262
|
+
})
|
|
263
|
+
})
|
|
@@ -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
|
-
)
|