chore: enforce effect simplifications (#43979)

This commit is contained in:
Kit Langton 2026-08-21 15:48:27 -04:00 committed by GitHub
parent fa1b4ef7ec
commit e945ddf80e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 861 additions and 66 deletions

View file

@ -0,0 +1,17 @@
# Effect simplification rules
The dedicated config scans both TypeScript and TSX with equivalent rules. The
generic ast-grep config intentionally does not load these rules.
Ignored callback parameters are supported only when they are simple ASCII
underscore-prefixed identifiers such as `_`, `_error`, or `_value`, optionally
with a type annotation. A rule reports the callback only when the exact target
subtree contains no underscore-prefixed identifier-like node, proving the
binding is unused without relying on unavailable TypeScript scope resolution.
This proof is intentionally conservative. Destructured, defaulted, rest,
optional, non-underscore, and Unicode parameters are excluded. A nested binding
or property whose name starts with `_` also suppresses the diagnostic, even when
it does not reference the callback parameter. Regular-function callbacks are
excluded when their target captures `this`, `arguments`, or a meta-property such
as `new.target` or `import.meta`.

View file

@ -0,0 +1,28 @@
id: no-effect-and-then-succeed-undefined
snapshots:
Effect.andThen(effect, Effect.succeed(undefined)):
labels:
- source: Effect.andThen(effect, Effect.succeed(undefined))
style: primary
start: 0
end: 49
? |
effect.pipe(
Effect.andThen(
Effect.succeed(undefined),
),
)
: labels:
- source: |-
Effect.andThen(
Effect.succeed(undefined),
)
style: primary
start: 15
end: 65
effect.pipe(Effect.andThen(Effect.succeed(undefined))):
labels:
- source: Effect.andThen(Effect.succeed(undefined))
style: primary
start: 12
end: 53

View file

@ -0,0 +1,32 @@
id: no-effect-and-then-succeed-undefined-tsx
snapshots:
? |
const view = (
<Widget
value={effect.pipe(
Effect.andThen(
Effect.succeed(undefined),
),
)}
/>
)
: labels:
- source: |-
Effect.andThen(
Effect.succeed(undefined),
)
style: primary
start: 55
end: 113
const view = <Widget value={Effect.andThen(effect, Effect.succeed(undefined))} />:
labels:
- source: Effect.andThen(effect, Effect.succeed(undefined))
style: primary
start: 28
end: 77
const view = <Widget value={effect.pipe(Effect.andThen(Effect.succeed(undefined)))} />:
labels:
- source: Effect.andThen(Effect.succeed(undefined))
style: primary
start: 40
end: 81

View file

@ -0,0 +1,95 @@
id: no-effect-catch-succeed
snapshots:
'Effect.catch(effect, () => Effect.succeed({ reason: "fallback" }))':
labels:
- source: 'Effect.catch(effect, () => Effect.succeed({ reason: "fallback" }))'
style: primary
start: 0
end: 66
? |
effect.pipe(
Effect.catch(() => Effect.succeed([] as string[])),
)
: labels:
- source: Effect.catch(() => Effect.succeed([] as string[]))
style: primary
start: 15
end: 65
effect.pipe(Effect.catch(() => Effect.succeed(fallback))):
labels:
- source: Effect.catch(() => Effect.succeed(fallback))
style: primary
start: 12
end: 56
effect.pipe(Effect.catch(() => { return Effect.succeed(fallback) })):
labels:
- source: Effect.catch(() => { return Effect.succeed(fallback) })
style: primary
start: 12
end: 67
effect.pipe(Effect.catch((_) => Effect.succeed(fallback))):
labels:
- source: Effect.catch((_) => Effect.succeed(fallback))
style: primary
start: 12
end: 57
"effect.pipe(Effect.catch((_cause: Cause) => Effect.succeed(fallback)))":
labels:
- source: "Effect.catch((_cause: Cause) => Effect.succeed(fallback))"
style: primary
start: 12
end: 69
effect.pipe(Effect.catch((_error) => Effect.succeed(fallback))):
labels:
- source: Effect.catch((_error) => Effect.succeed(fallback))
style: primary
start: 12
end: 62
"effect.pipe(Effect.catch((_error: Error) => { return Effect.succeed(fallback) }))":
labels:
- source: "Effect.catch((_error: Error) => { return Effect.succeed(fallback) })"
style: primary
start: 12
end: 80
effect.pipe(Effect.catch(_ => Effect.succeed(fallback))):
labels:
- source: Effect.catch(_ => Effect.succeed(fallback))
style: primary
start: 12
end: 55
effect.pipe(Effect.catch(_ => { return Effect.succeed(fallback) })):
labels:
- source: Effect.catch(_ => { return Effect.succeed(fallback) })
style: primary
start: 12
end: 66
effect.pipe(Effect.catch(_error => { return Effect.succeed(fallback) })):
labels:
- source: Effect.catch(_error => { return Effect.succeed(fallback) })
style: primary
start: 12
end: 71
effect.pipe(Effect.catch(function () { return Effect.succeed(fallback) })):
labels:
- source: Effect.catch(function () { return Effect.succeed(fallback) })
style: primary
start: 12
end: 73
effect.pipe(Effect.catch(function (_) { return Effect.succeed(fallback) })):
labels:
- source: Effect.catch(function (_) { return Effect.succeed(fallback) })
style: primary
start: 12
end: 74
effect.pipe(Effect.catch(function (_error) { return Effect.succeed(fallback) })):
labels:
- source: Effect.catch(function (_error) { return Effect.succeed(fallback) })
style: primary
start: 12
end: 79
"effect.pipe(Effect.catch(function (_error: Error) { return Effect.succeed(fallback) }))":
labels:
- source: "Effect.catch(function (_error: Error) { return Effect.succeed(fallback) })"
style: primary
start: 12
end: 86

View file

@ -0,0 +1,38 @@
id: no-effect-catch-succeed-tsx
snapshots:
const view = <Widget value={Effect.catch(effect, () => Effect.succeed(fallback))} />:
labels:
- source: Effect.catch(effect, () => Effect.succeed(fallback))
style: primary
start: 28
end: 80
const view = <Widget value={effect.pipe(Effect.catch(() => Effect.succeed(fallback)))} />:
labels:
- source: Effect.catch(() => Effect.succeed(fallback))
style: primary
start: 40
end: 84
const view = <Widget value={effect.pipe(Effect.catch((_error) => Effect.succeed(fallback)))} />:
labels:
- source: Effect.catch((_error) => Effect.succeed(fallback))
style: primary
start: 40
end: 90
"const view = <Widget value={effect.pipe(Effect.catch((_error: Error) => Effect.succeed(fallback)))} />":
labels:
- source: "Effect.catch((_error: Error) => Effect.succeed(fallback))"
style: primary
start: 40
end: 97
const view = <Widget value={effect.pipe(Effect.catch(_error => { return Effect.succeed(fallback) }))} />:
labels:
- source: Effect.catch(_error => { return Effect.succeed(fallback) })
style: primary
start: 40
end: 99
const view = <Widget value={effect.pipe(Effect.catch(function (_error) { return Effect.succeed(fallback) }))} />:
labels:
- source: Effect.catch(function (_error) { return Effect.succeed(fallback) })
style: primary
start: 40
end: 107

View file

@ -0,0 +1,100 @@
id: no-effect-flat-map-suspend
snapshots:
Effect.flatMap(effect, () => Effect.suspend(() => next)):
labels:
- source: Effect.flatMap(effect, () => Effect.suspend(() => next))
style: primary
start: 0
end: 56
? |
effect.pipe(
Effect.flatMap(() =>
Effect.suspend(() => next),
),
)
: labels:
- source: |-
Effect.flatMap(() =>
Effect.suspend(() => next),
)
style: primary
start: 15
end: 71
effect.pipe(Effect.flatMap(() => Effect.suspend(() => next))):
labels:
- source: Effect.flatMap(() => Effect.suspend(() => next))
style: primary
start: 12
end: 60
effect.pipe(Effect.flatMap(() => { return Effect.suspend(() => next) })):
labels:
- source: Effect.flatMap(() => { return Effect.suspend(() => next) })
style: primary
start: 12
end: 71
effect.pipe(Effect.flatMap((_) => Effect.suspend(() => next))):
labels:
- source: Effect.flatMap((_) => Effect.suspend(() => next))
style: primary
start: 12
end: 61
effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => next))):
labels:
- source: Effect.flatMap((_value) => Effect.suspend(() => next))
style: primary
start: 12
end: 66
"effect.pipe(Effect.flatMap((_value: Value) => Effect.suspend(() => next)))":
labels:
- source: "Effect.flatMap((_value: Value) => Effect.suspend(() => next))"
style: primary
start: 12
end: 73
"effect.pipe(Effect.flatMap((_value: Value) => { return Effect.suspend(() => next) }))":
labels:
- source: "Effect.flatMap((_value: Value) => { return Effect.suspend(() => next) })"
style: primary
start: 12
end: 84
effect.pipe(Effect.flatMap(_ => Effect.suspend(() => next))):
labels:
- source: Effect.flatMap(_ => Effect.suspend(() => next))
style: primary
start: 12
end: 59
effect.pipe(Effect.flatMap(_ => { return Effect.suspend(() => next) })):
labels:
- source: Effect.flatMap(_ => { return Effect.suspend(() => next) })
style: primary
start: 12
end: 70
effect.pipe(Effect.flatMap(_value => { return Effect.suspend(() => next) })):
labels:
- source: Effect.flatMap(_value => { return Effect.suspend(() => next) })
style: primary
start: 12
end: 75
effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => next) })):
labels:
- source: Effect.flatMap(function () { return Effect.suspend(() => next) })
style: primary
start: 12
end: 77
effect.pipe(Effect.flatMap(function (_) { return Effect.suspend(() => next) })):
labels:
- source: Effect.flatMap(function (_) { return Effect.suspend(() => next) })
style: primary
start: 12
end: 78
effect.pipe(Effect.flatMap(function (_value) { return Effect.suspend(() => next) })):
labels:
- source: Effect.flatMap(function (_value) { return Effect.suspend(() => next) })
style: primary
start: 12
end: 83
"effect.pipe(Effect.flatMap(function (_value: Value) { return Effect.suspend(() => next) }))":
labels:
- source: "Effect.flatMap(function (_value: Value) { return Effect.suspend(() => next) })"
style: primary
start: 12
end: 90

View file

@ -0,0 +1,38 @@
id: no-effect-flat-map-suspend-tsx
snapshots:
const view = <Widget value={Effect.flatMap(effect, () => Effect.suspend(() => next))} />:
labels:
- source: Effect.flatMap(effect, () => Effect.suspend(() => next))
style: primary
start: 28
end: 84
const view = <Widget value={effect.pipe(Effect.flatMap(() => Effect.suspend(() => next)))} />:
labels:
- source: Effect.flatMap(() => Effect.suspend(() => next))
style: primary
start: 40
end: 88
const view = <Widget value={effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => next)))} />:
labels:
- source: Effect.flatMap((_value) => Effect.suspend(() => next))
style: primary
start: 40
end: 94
"const view = <Widget value={effect.pipe(Effect.flatMap((_value: Value) => Effect.suspend(() => next)))} />":
labels:
- source: "Effect.flatMap((_value: Value) => Effect.suspend(() => next))"
style: primary
start: 40
end: 101
const view = <Widget value={effect.pipe(Effect.flatMap(_value => { return Effect.suspend(() => next) }))} />:
labels:
- source: Effect.flatMap(_value => { return Effect.suspend(() => next) })
style: primary
start: 40
end: 103
const view = <Widget value={effect.pipe(Effect.flatMap(function (_value) { return Effect.suspend(() => next) }))} />:
labels:
- source: Effect.flatMap(function (_value) { return Effect.suspend(() => next) })
style: primary
start: 40
end: 111

View file

@ -0,0 +1,21 @@
id: no-effect-and-then-succeed-undefined
valid:
- effect.pipe(Effect.as(undefined))
- Effect.as(effect, undefined)
- effect.pipe(Effect.asVoid)
- effect.pipe(Effect.andThen(Effect.succeed(null)))
- effect.pipe(Effect.andThen(() => Effect.succeed(undefined)))
- effect.pipe(Effect.andThen(Effect.sync(() => undefined)))
- effect.pipe(Stream.andThen(Effect.succeed(undefined)))
- Effect.andThen(effect, options, Effect.succeed(undefined))
- Effect.andThen(first, second, third, Effect.succeed(undefined))
- Effect.succeed(undefined)
invalid:
- effect.pipe(Effect.andThen(Effect.succeed(undefined)))
- Effect.andThen(effect, Effect.succeed(undefined))
- |
effect.pipe(
Effect.andThen(
Effect.succeed(undefined),
),
)

View file

@ -0,0 +1,21 @@
id: no-effect-and-then-succeed-undefined-tsx
valid:
- const view = <Widget value={effect.pipe(Effect.as(undefined))} />
- const view = <Widget value={Effect.as(effect, undefined)} />
- const view = <Widget value={effect.pipe(Effect.asVoid)} />
- const view = <Widget value={effect.pipe(Effect.andThen(Effect.succeed(null)))} />
- const view = <Widget value={Effect.andThen(effect, options, Effect.succeed(undefined))} />
- const view = <Widget value={Stream.andThen(effect, Effect.succeed(undefined))} />
invalid:
- const view = <Widget value={effect.pipe(Effect.andThen(Effect.succeed(undefined)))} />
- const view = <Widget value={Effect.andThen(effect, Effect.succeed(undefined))} />
- |
const view = (
<Widget
value={effect.pipe(
Effect.andThen(
Effect.succeed(undefined),
),
)}
/>
)

View file

@ -0,0 +1,49 @@
id: no-effect-catch-succeed
valid:
- effect.pipe(Effect.orElseSucceed(() => fallback))
- Effect.orElseSucceed(effect, () => fallback)
- effect.pipe(Effect.catch((error) => Effect.succeed(error)))
- effect.pipe(Effect.catch((error) => Effect.succeed(fallback)))
- effect.pipe(Effect.catch((_) => Effect.succeed(_)))
- effect.pipe(Effect.catch((_) => Effect.succeed(use(_))))
- effect.pipe(Effect.catch((_) => { return Effect.succeed(_) }))
- effect.pipe(Effect.catch((_error) => Effect.succeed(_error)))
- effect.pipe(Effect.catch((_error) => Effect.succeed({ _error })))
- "effect.pipe(Effect.catch((_error: Error) => Effect.succeed(use(_error))))"
- effect.pipe(Effect.catch((_error) => Effect.succeed(values.map((_error) => _error))))
- effect.pipe(Effect.catch(({ message }) => Effect.succeed(fallback)))
- effect.pipe(Effect.catch((_error = fallbackError) => Effect.succeed(fallback)))
- effect.pipe(Effect.catch((..._errors) => Effect.succeed(fallback)))
- "effect.pipe(Effect.catch((_error?: Error) => Effect.succeed(fallback)))"
- effect.pipe(Effect.catch(() => Effect.void))
- effect.pipe(Effect.catch(() => Effect.sync(fallback)))
- effect.pipe(Other.catch(() => Effect.succeed(fallback)))
- Effect.catch(effect, options, () => Effect.succeed(fallback))
- Effect.catch(first, second, third, () => Effect.succeed(fallback))
- effect.pipe(Effect.catch(() => { log(); return Effect.succeed(fallback) }))
- effect.pipe(Effect.catch(function () { return Effect.succeed(this.fallback) }))
- effect.pipe(Effect.catch(function () { return Effect.succeed(arguments[0]) }))
- effect.pipe(Effect.catch(function () { return Effect.succeed(new.target) }))
- effect.pipe(Effect.catch(function () { return Effect.succeed(import.meta) }))
- effect.pipe(Effect.catch(function (_) { return Effect.succeed(_) }))
- "effect.pipe(Effect.catch(function (_error: Error) { return Effect.succeed(_error) }))"
- effect.pipe(Effect.catch(function (_error) { return Effect.succeed(new.target) }))
invalid:
- effect.pipe(Effect.catch(() => Effect.succeed(fallback)))
- 'Effect.catch(effect, () => Effect.succeed({ reason: "fallback" }))'
- effect.pipe(Effect.catch((_) => Effect.succeed(fallback)))
- effect.pipe(Effect.catch(_ => Effect.succeed(fallback)))
- effect.pipe(Effect.catch(_ => { return Effect.succeed(fallback) }))
- effect.pipe(Effect.catch(() => { return Effect.succeed(fallback) }))
- effect.pipe(Effect.catch(function () { return Effect.succeed(fallback) }))
- effect.pipe(Effect.catch(function (_) { return Effect.succeed(fallback) }))
- effect.pipe(Effect.catch((_error) => Effect.succeed(fallback)))
- effect.pipe(Effect.catch(_error => { return Effect.succeed(fallback) }))
- "effect.pipe(Effect.catch((_cause: Cause) => Effect.succeed(fallback)))"
- "effect.pipe(Effect.catch((_error: Error) => { return Effect.succeed(fallback) }))"
- effect.pipe(Effect.catch(function (_error) { return Effect.succeed(fallback) }))
- "effect.pipe(Effect.catch(function (_error: Error) { return Effect.succeed(fallback) }))"
- |
effect.pipe(
Effect.catch(() => Effect.succeed([] as string[])),
)

View file

@ -0,0 +1,21 @@
id: no-effect-catch-succeed-tsx
valid:
- const view = <Widget value={effect.pipe(Effect.orElseSucceed(() => fallback))} />
- const view = <Widget value={effect.pipe(Effect.catch((error) => Effect.succeed(error)))} />
- const view = <Widget value={effect.pipe(Effect.catch((error) => Effect.succeed(fallback)))} />
- const view = <Widget value={effect.pipe(Effect.catch((_error) => Effect.succeed(_error)))} />
- const view = <Widget value={effect.pipe(Effect.catch((_error) => Effect.succeed({ _error })))} />
- "const view = <Widget value={effect.pipe(Effect.catch((_error: Error) => Effect.succeed(use(_error))))} />"
- const view = <Widget value={effect.pipe(Effect.catch((_error) => Effect.succeed(values.map((_error) => _error))))} />
- const view = <Widget value={effect.pipe(Effect.catch(({ message }) => Effect.succeed(fallback)))} />
- const view = <Widget value={Effect.catch(effect, options, () => Effect.succeed(fallback))} />
- const view = <Widget value={effect.pipe(Effect.catch(() => { log(); return Effect.succeed(fallback) }))} />
- const view = <Widget value={effect.pipe(Effect.catch(function () { return Effect.succeed(new.target) }))} />
- const view = <Widget value={effect.pipe(Effect.catch(function () { return Effect.succeed(import.meta) }))} />
invalid:
- const view = <Widget value={effect.pipe(Effect.catch(() => Effect.succeed(fallback)))} />
- const view = <Widget value={Effect.catch(effect, () => Effect.succeed(fallback))} />
- const view = <Widget value={effect.pipe(Effect.catch((_error) => Effect.succeed(fallback)))} />
- const view = <Widget value={effect.pipe(Effect.catch(_error => { return Effect.succeed(fallback) }))} />
- "const view = <Widget value={effect.pipe(Effect.catch((_error: Error) => Effect.succeed(fallback)))} />"
- const view = <Widget value={effect.pipe(Effect.catch(function (_error) { return Effect.succeed(fallback) }))} />

View file

@ -0,0 +1,52 @@
id: no-effect-flat-map-suspend
valid:
- effect.pipe(Effect.andThen(Effect.suspend(() => next)))
- Effect.andThen(effect, Effect.suspend(() => next))
- effect.pipe(Effect.flatMap((value) => Effect.suspend(() => use(value))))
- effect.pipe(Effect.flatMap((value) => Effect.suspend(() => next)))
- effect.pipe(Effect.flatMap((_) => Effect.suspend(() => use(_))))
- effect.pipe(Effect.flatMap((_) => { return Effect.suspend(() => use(_)) }))
- effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => use(_value))))
- effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => ({ _value }))))
- "effect.pipe(Effect.flatMap((_value: Value) => Effect.suspend(() => use(_value))))"
- effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => values.map((_value) => use(_value)))))
- effect.pipe(Effect.flatMap(({ value }) => Effect.suspend(() => next)))
- effect.pipe(Effect.flatMap((_value = fallback) => Effect.suspend(() => next)))
- effect.pipe(Effect.flatMap((..._values) => Effect.suspend(() => next)))
- "effect.pipe(Effect.flatMap((_value?: Value) => Effect.suspend(() => next)))"
- effect.pipe(Effect.flatMap(() => Effect.suspend(thunk)))
- effect.pipe(Effect.flatMap(() => Effect.suspend(getThunk())))
- effect.pipe(Effect.flatMap(() => Effect.suspend(function thunk() { return next })))
- effect.pipe(Effect.flatMap(() => Effect.sync(thunk)))
- effect.pipe(Stream.flatMap(() => Effect.suspend(() => next)))
- Effect.flatMap(effect, options, () => Effect.suspend(() => next))
- Effect.flatMap(first, second, third, () => Effect.suspend(() => next))
- effect.pipe(Effect.flatMap(() => { log(); return Effect.suspend(() => next) }))
- effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => this.next) }))
- effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => arguments[0]) }))
- effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => new.target) }))
- effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => import.meta) }))
- effect.pipe(Effect.flatMap(function (_) { return Effect.suspend(() => use(_)) }))
- "effect.pipe(Effect.flatMap(function (_value: Value) { return Effect.suspend(() => use(_value)) }))"
- effect.pipe(Effect.flatMap(function (_value) { return Effect.suspend(() => new.target) }))
invalid:
- effect.pipe(Effect.flatMap(() => Effect.suspend(() => next)))
- Effect.flatMap(effect, () => Effect.suspend(() => next))
- effect.pipe(Effect.flatMap((_) => Effect.suspend(() => next)))
- effect.pipe(Effect.flatMap(_ => Effect.suspend(() => next)))
- effect.pipe(Effect.flatMap(_ => { return Effect.suspend(() => next) }))
- effect.pipe(Effect.flatMap(() => { return Effect.suspend(() => next) }))
- effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => next) }))
- effect.pipe(Effect.flatMap(function (_) { return Effect.suspend(() => next) }))
- effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => next)))
- effect.pipe(Effect.flatMap(_value => { return Effect.suspend(() => next) }))
- "effect.pipe(Effect.flatMap((_value: Value) => Effect.suspend(() => next)))"
- "effect.pipe(Effect.flatMap((_value: Value) => { return Effect.suspend(() => next) }))"
- effect.pipe(Effect.flatMap(function (_value) { return Effect.suspend(() => next) }))
- "effect.pipe(Effect.flatMap(function (_value: Value) { return Effect.suspend(() => next) }))"
- |
effect.pipe(
Effect.flatMap(() =>
Effect.suspend(() => next),
),
)

View file

@ -0,0 +1,21 @@
id: no-effect-flat-map-suspend-tsx
valid:
- const view = <Widget value={effect.pipe(Effect.andThen(Effect.suspend(() => next)))} />
- const view = <Widget value={effect.pipe(Effect.flatMap((value) => Effect.suspend(() => use(value))))} />
- const view = <Widget value={effect.pipe(Effect.flatMap((value) => Effect.suspend(() => next)))} />
- const view = <Widget value={effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => use(_value))))} />
- const view = <Widget value={effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => ({ _value }))))} />
- "const view = <Widget value={effect.pipe(Effect.flatMap((_value: Value) => Effect.suspend(() => use(_value))))} />"
- const view = <Widget value={effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => values.map((_value) => use(_value)))))} />
- const view = <Widget value={effect.pipe(Effect.flatMap(({ value }) => Effect.suspend(() => next)))} />
- const view = <Widget value={Effect.flatMap(effect, options, () => Effect.suspend(() => next))} />
- const view = <Widget value={effect.pipe(Effect.flatMap(() => Effect.suspend(thunk)))} />
- const view = <Widget value={effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => new.target) }))} />
- const view = <Widget value={effect.pipe(Effect.flatMap(function () { return Effect.suspend(() => import.meta) }))} />
invalid:
- const view = <Widget value={effect.pipe(Effect.flatMap(() => Effect.suspend(() => next)))} />
- const view = <Widget value={Effect.flatMap(effect, () => Effect.suspend(() => next))} />
- const view = <Widget value={effect.pipe(Effect.flatMap((_value) => Effect.suspend(() => next)))} />
- const view = <Widget value={effect.pipe(Effect.flatMap(_value => { return Effect.suspend(() => next) }))} />
- "const view = <Widget value={effect.pipe(Effect.flatMap((_value: Value) => Effect.suspend(() => next)))} />"
- const view = <Widget value={effect.pipe(Effect.flatMap(function (_value) { return Effect.suspend(() => next) }))} />

View file

@ -0,0 +1,9 @@
id: no-effect-and-then-succeed-undefined-tsx
language: Tsx
message: Use Effect.as(undefined) to preserve the exact undefined success type.
severity: error
rule:
all:
- pattern: Effect.andThen($$$SELF, Effect.succeed(undefined))
- not:
pattern: Effect.andThen($_FIRST, $_SECOND, $$$REST)

View file

@ -0,0 +1,9 @@
id: no-effect-and-then-succeed-undefined
language: TypeScript
message: Use Effect.as(undefined) to preserve the exact undefined success type.
severity: error
rule:
all:
- pattern: Effect.andThen($$$SELF, Effect.succeed(undefined))
- not:
pattern: Effect.andThen($_FIRST, $_SECOND, $$$REST)

View file

@ -0,0 +1,61 @@
id: no-effect-catch-succeed-tsx
language: Tsx
message: Use Effect.orElseSucceed for constant failure recovery.
severity: error
rule:
all:
- any:
- pattern: Effect.catch($$$SELF, () => Effect.succeed($VALUE))
- pattern: Effect.catch($$$SELF, () => { return Effect.succeed($VALUE) })
- pattern: Effect.catch($$$SELF, ($IGNORED) => Effect.succeed($IGNORED_VALUE))
- pattern: Effect.catch($$$SELF, ($IGNORED) => { return Effect.succeed($IGNORED_VALUE) })
- pattern: Effect.catch($$$SELF, $IGNORED => Effect.succeed($IGNORED_VALUE))
- pattern: Effect.catch($$$SELF, $IGNORED => { return Effect.succeed($IGNORED_VALUE) })
- pattern: "Effect.catch($$$SELF, ($IGNORED: $_TYPE) => Effect.succeed($IGNORED_VALUE))"
- pattern: "Effect.catch($$$SELF, ($IGNORED: $_TYPE) => { return Effect.succeed($IGNORED_VALUE) })"
- pattern: Effect.catch($$$SELF, function () { return Effect.succeed($FUNCTION_VALUE) })
- pattern: Effect.catch($$$SELF, function ($FUNCTION_IGNORED) { return Effect.succeed($FUNCTION_IGNORED_VALUE) })
- pattern: "Effect.catch($$$SELF, function ($FUNCTION_IGNORED: $_TYPE) { return Effect.succeed($FUNCTION_IGNORED_VALUE) })"
- not:
pattern: Effect.catch($_FIRST, $_SECOND, $$$REST)
constraints:
IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
IGNORED_VALUE:
all:
- not:
regex: ^_[A-Za-z0-9_$]*$
- not:
has:
regex: ^_[A-Za-z0-9_$]*$
stopBy: end
FUNCTION_VALUE:
all:
- not:
any:
- regex: ^arguments$
- kind: this
- kind: meta_property
- not:
has:
any:
- regex: ^arguments$
- kind: this
- kind: meta_property
stopBy: end
FUNCTION_IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
FUNCTION_IGNORED_VALUE:
all:
- not:
any:
- regex: ^(_[A-Za-z0-9_$]*|arguments)$
- kind: this
- kind: meta_property
- not:
has:
any:
- regex: ^(_[A-Za-z0-9_$]*|arguments)$
- kind: this
- kind: meta_property
stopBy: end

View file

@ -0,0 +1,61 @@
id: no-effect-catch-succeed
language: TypeScript
message: Use Effect.orElseSucceed for constant failure recovery.
severity: error
rule:
all:
- any:
- pattern: Effect.catch($$$SELF, () => Effect.succeed($VALUE))
- pattern: Effect.catch($$$SELF, () => { return Effect.succeed($VALUE) })
- pattern: Effect.catch($$$SELF, ($IGNORED) => Effect.succeed($IGNORED_VALUE))
- pattern: Effect.catch($$$SELF, ($IGNORED) => { return Effect.succeed($IGNORED_VALUE) })
- pattern: Effect.catch($$$SELF, $IGNORED => Effect.succeed($IGNORED_VALUE))
- pattern: Effect.catch($$$SELF, $IGNORED => { return Effect.succeed($IGNORED_VALUE) })
- pattern: "Effect.catch($$$SELF, ($IGNORED: $_TYPE) => Effect.succeed($IGNORED_VALUE))"
- pattern: "Effect.catch($$$SELF, ($IGNORED: $_TYPE) => { return Effect.succeed($IGNORED_VALUE) })"
- pattern: Effect.catch($$$SELF, function () { return Effect.succeed($FUNCTION_VALUE) })
- pattern: Effect.catch($$$SELF, function ($FUNCTION_IGNORED) { return Effect.succeed($FUNCTION_IGNORED_VALUE) })
- pattern: "Effect.catch($$$SELF, function ($FUNCTION_IGNORED: $_TYPE) { return Effect.succeed($FUNCTION_IGNORED_VALUE) })"
- not:
pattern: Effect.catch($_FIRST, $_SECOND, $$$REST)
constraints:
IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
IGNORED_VALUE:
all:
- not:
regex: ^_[A-Za-z0-9_$]*$
- not:
has:
regex: ^_[A-Za-z0-9_$]*$
stopBy: end
FUNCTION_VALUE:
all:
- not:
any:
- regex: ^arguments$
- kind: this
- kind: meta_property
- not:
has:
any:
- regex: ^arguments$
- kind: this
- kind: meta_property
stopBy: end
FUNCTION_IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
FUNCTION_IGNORED_VALUE:
all:
- not:
any:
- regex: ^(_[A-Za-z0-9_$]*|arguments)$
- kind: this
- kind: meta_property
- not:
has:
any:
- regex: ^(_[A-Za-z0-9_$]*|arguments)$
- kind: this
- kind: meta_property
stopBy: end

View file

@ -0,0 +1,54 @@
id: no-effect-flat-map-suspend-tsx
language: Tsx
message: Use effect-form Effect.andThen when the previous value is ignored.
severity: error
rule:
all:
- any:
- pattern: Effect.flatMap($$$SELF, () => Effect.suspend($THUNK))
- pattern: Effect.flatMap($$$SELF, () => { return Effect.suspend($THUNK) })
- pattern: Effect.flatMap($$$SELF, ($IGNORED) => Effect.suspend($IGNORED_THUNK))
- pattern: Effect.flatMap($$$SELF, ($IGNORED) => { return Effect.suspend($IGNORED_THUNK) })
- pattern: Effect.flatMap($$$SELF, $IGNORED => Effect.suspend($IGNORED_THUNK))
- pattern: Effect.flatMap($$$SELF, $IGNORED => { return Effect.suspend($IGNORED_THUNK) })
- pattern: "Effect.flatMap($$$SELF, ($IGNORED: $_TYPE) => Effect.suspend($IGNORED_THUNK))"
- pattern: "Effect.flatMap($$$SELF, ($IGNORED: $_TYPE) => { return Effect.suspend($IGNORED_THUNK) })"
- pattern: Effect.flatMap($$$SELF, function () { return Effect.suspend($FUNCTION_THUNK) })
- pattern: Effect.flatMap($$$SELF, function ($FUNCTION_IGNORED) { return Effect.suspend($FUNCTION_IGNORED_THUNK) })
- pattern: "Effect.flatMap($$$SELF, function ($FUNCTION_IGNORED: $_TYPE) { return Effect.suspend($FUNCTION_IGNORED_THUNK) })"
- not:
pattern: Effect.flatMap($_FIRST, $_SECOND, $$$REST)
constraints:
THUNK:
kind: arrow_function
IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
IGNORED_THUNK:
all:
- kind: arrow_function
- not:
has:
regex: ^_[A-Za-z0-9_$]*$
stopBy: end
FUNCTION_THUNK:
all:
- kind: arrow_function
- not:
has:
any:
- regex: ^arguments$
- kind: this
- kind: meta_property
stopBy: end
FUNCTION_IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
FUNCTION_IGNORED_THUNK:
all:
- kind: arrow_function
- not:
has:
any:
- regex: ^(_[A-Za-z0-9_$]*|arguments)$
- kind: this
- kind: meta_property
stopBy: end

View file

@ -0,0 +1,54 @@
id: no-effect-flat-map-suspend
language: TypeScript
message: Use effect-form Effect.andThen when the previous value is ignored.
severity: error
rule:
all:
- any:
- pattern: Effect.flatMap($$$SELF, () => Effect.suspend($THUNK))
- pattern: Effect.flatMap($$$SELF, () => { return Effect.suspend($THUNK) })
- pattern: Effect.flatMap($$$SELF, ($IGNORED) => Effect.suspend($IGNORED_THUNK))
- pattern: Effect.flatMap($$$SELF, ($IGNORED) => { return Effect.suspend($IGNORED_THUNK) })
- pattern: Effect.flatMap($$$SELF, $IGNORED => Effect.suspend($IGNORED_THUNK))
- pattern: Effect.flatMap($$$SELF, $IGNORED => { return Effect.suspend($IGNORED_THUNK) })
- pattern: "Effect.flatMap($$$SELF, ($IGNORED: $_TYPE) => Effect.suspend($IGNORED_THUNK))"
- pattern: "Effect.flatMap($$$SELF, ($IGNORED: $_TYPE) => { return Effect.suspend($IGNORED_THUNK) })"
- pattern: Effect.flatMap($$$SELF, function () { return Effect.suspend($FUNCTION_THUNK) })
- pattern: Effect.flatMap($$$SELF, function ($FUNCTION_IGNORED) { return Effect.suspend($FUNCTION_IGNORED_THUNK) })
- pattern: "Effect.flatMap($$$SELF, function ($FUNCTION_IGNORED: $_TYPE) { return Effect.suspend($FUNCTION_IGNORED_THUNK) })"
- not:
pattern: Effect.flatMap($_FIRST, $_SECOND, $$$REST)
constraints:
THUNK:
kind: arrow_function
IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
IGNORED_THUNK:
all:
- kind: arrow_function
- not:
has:
regex: ^_[A-Za-z0-9_$]*$
stopBy: end
FUNCTION_THUNK:
all:
- kind: arrow_function
- not:
has:
any:
- regex: ^arguments$
- kind: this
- kind: meta_property
stopBy: end
FUNCTION_IGNORED:
regex: ^_[A-Za-z0-9_$]*(?:\s*:\s*.+)?$
FUNCTION_IGNORED_THUNK:
all:
- kind: arrow_function
- not:
has:
any:
- regex: ^(_[A-Za-z0-9_$]*|arguments)$
- kind: this
- kind: meta_property
stopBy: end

View file

@ -0,0 +1,4 @@
ruleDirs:
- rules
testConfigs:
- testDir: rule-tests