mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-22 00:03:40 +00:00
chore: enforce effect simplifications (#43979)
This commit is contained in:
parent
fa1b4ef7ec
commit
e945ddf80e
57 changed files with 861 additions and 66 deletions
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
|
|
@ -50,6 +50,14 @@ jobs:
|
|||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Test Effect simplification rules
|
||||
if: runner.os == 'Linux'
|
||||
run: bun run test:effect-simplification-rules
|
||||
|
||||
- name: Check Effect simplifications
|
||||
if: runner.os == 'Linux'
|
||||
run: bun run lint:effect-simplifications
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
git config --global user.email "bot@opencode.ai"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
"bench:devex": "bun run --cwd packages/app test:bench:devex",
|
||||
"lint": "oxlint",
|
||||
"lint:effect-patterns": "ast-grep scan -c script/ast-grep/sgconfig.yml packages/util/src packages/core/src packages/server/src packages/protocol/src packages/cli/src",
|
||||
"lint:effect-simplifications": "ast-grep scan -c script/ast-grep/effect-simplifications/sgconfig.yml --off=unused-suppression packages",
|
||||
"test:lint-rules": "ast-grep test -c script/ast-grep/sgconfig.yml",
|
||||
"test:effect-simplification-rules": "ast-grep test -c script/ast-grep/effect-simplifications/sgconfig.yml",
|
||||
"typecheck": "bun turbo typecheck --concurrency=3",
|
||||
"typecheck:profile": "bun script/profile-typecheck.ts",
|
||||
"typecheck:profile:packages": "bun script/profile-typecheck-packages.ts",
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ const responseError = Effect.fn("RecordingEnv.responseError")(function* (
|
|||
response: HttpClientResponse.HttpClientResponse,
|
||||
) {
|
||||
if (response.status >= 200 && response.status < 300) return undefined
|
||||
const body = yield* response.text.pipe(Effect.catch(() => Effect.succeed("")))
|
||||
const body = yield* response.text.pipe(Effect.orElseSucceed(() => ""))
|
||||
return `${response.status}${body ? `: ${body.slice(0, 180)}` : ""}`
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const layer = Layer.effect(
|
|||
const file = path.join(global.config, "cli.json")
|
||||
|
||||
const readJson = Effect.fnUntraced(function* () {
|
||||
const text = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const text = yield* fs.readFileString(file).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (text === undefined) return undefined
|
||||
const errors: ParseError[] = []
|
||||
const value: any = parse(text, errors, { allowTrailingComma: true })
|
||||
|
|
@ -87,7 +87,7 @@ export const layer = Layer.effect(
|
|||
const next = produce(current, update)
|
||||
const edits = changes(current, next)
|
||||
if (!edits.length) return current
|
||||
const text = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed("{}")))
|
||||
const text = yield* fs.readFileString(file).pipe(Effect.orElseSucceed(() => "{}"))
|
||||
const updated = edits.reduce(
|
||||
(text, edit) =>
|
||||
applyEdits(
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ export function migrateV1(legacy: TuiConfigV1.Info | undefined, kv: Record<strin
|
|||
|
||||
const readJson = Effect.fnUntraced(function* (target: string) {
|
||||
const fs = yield* FileSystem.FileSystem
|
||||
const text = yield* fs.readFileString(target).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const text = yield* fs.readFileString(target).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (text === undefined) return undefined
|
||||
const errors: ParseError[] = []
|
||||
const value: any = parse(text, errors, { allowTrailingComma: true })
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ export const read = Effect.fn("cli.service-config.read")(function* () {
|
|||
if (legacyConfigFile) yield* migrateConfig(legacyConfigFile, configFile)
|
||||
return yield* fs.readFileString(configFile).pipe(
|
||||
Effect.flatMap(decodeInfo),
|
||||
Effect.catch(() => Effect.succeed({} as Info)),
|
||||
Effect.orElseSucceed(() => ({}) as Info),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export const layer = Layer.effect(
|
|||
const values = yield* Effect.forEach(["config.json", "opencode.json", "opencode.jsonc"], (name) =>
|
||||
fs.readFileString(path.join(global.config, name)).pipe(
|
||||
Effect.map(decodePolicy),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
),
|
||||
)
|
||||
return values.findLast((value) => value !== undefined) ?? true
|
||||
|
|
@ -63,7 +63,7 @@ export const layer = Layer.effect(
|
|||
stdout: result.stdout.toString("utf8"),
|
||||
stderr: result.stderr.toString("utf8"),
|
||||
})),
|
||||
Effect.catch(() => Effect.succeed({ code: 1, stdout: "", stderr: "" })),
|
||||
Effect.orElseSucceed(() => ({ code: 1, stdout: "", stderr: "" })),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ export const layer = (options?: Options) =>
|
|||
Stream.filterEffect((event) =>
|
||||
wellknown.entries().pipe(
|
||||
Effect.map((entries) => entries.some((entry) => entry.integrationID === event.data.integrationID)),
|
||||
Effect.catch(() => Effect.succeed(false)),
|
||||
Effect.orElseSucceed(() => false),
|
||||
),
|
||||
),
|
||||
Stream.runForEach(() =>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export const Plugin = define({
|
|||
return yield* Effect.forEach(files, (file) =>
|
||||
fs.readFileStringSafe(file.filepath).pipe(
|
||||
Effect.map((content) => (content ? decode(file, content) : undefined)),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
),
|
||||
).pipe(Effect.map((documents) => documents.filter((document): document is Document => document !== undefined)))
|
||||
})
|
||||
|
|
@ -176,7 +176,7 @@ function discover(fs: FSUtil.Interface, directory: string) {
|
|||
),
|
||||
).pipe(
|
||||
Effect.map((files) => files.flat()),
|
||||
Effect.catch(() => Effect.succeed([])),
|
||||
Effect.orElseSucceed(() => []),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,11 +86,11 @@ function loadDirectory(fs: FSUtil.Interface, directory: string) {
|
|||
return Effect.gen(function* () {
|
||||
const files = yield* fs
|
||||
.scan("{command,commands}/**/*.md", { cwd: directory, absolute: true, dot: true, symlink: true })
|
||||
.pipe(Effect.catch(() => Effect.succeed([] as string[])))
|
||||
.pipe(Effect.orElseSucceed(() => [] as string[]))
|
||||
return yield* Effect.forEach(files.toSorted(), (filepath) =>
|
||||
fs.readFileStringSafe(filepath).pipe(
|
||||
Effect.map((content) => (content === undefined ? undefined : decode(directory, filepath, content))),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
),
|
||||
).pipe(
|
||||
Effect.map((commands) =>
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ const scan = Effect.fn("ConfigPluginSource.scan")(function* (
|
|||
...operation,
|
||||
mtime: Option.getOrElse(info.mtime, () => new Date(0)).getTime(),
|
||||
})),
|
||||
Effect.catch(() => Effect.succeed(operation)),
|
||||
Effect.orElseSucceed(() => operation),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -35,14 +35,12 @@ const layer = Layer.effect(
|
|||
Effect.gen(function* () {
|
||||
if (location.vcs?.type === "git") {
|
||||
const resolved = (yield* git.repo.discover(location.directory))?.gitDirectory
|
||||
const vcs = resolved
|
||||
? yield* fs.realPath(resolved).pipe(Effect.catch(() => Effect.succeed(resolved)))
|
||||
: undefined
|
||||
const vcs = resolved ? yield* fs.realPath(resolved).pipe(Effect.orElseSucceed(() => resolved)) : undefined
|
||||
if (vcs) return { path: path.join(vcs, "HEAD"), aliases: [".git", vcs, ...(resolved ? [resolved] : [])] }
|
||||
}
|
||||
if (location.vcs?.type === "hg") {
|
||||
const store = location.vcs.store
|
||||
const vcs = yield* fs.realPath(store).pipe(Effect.catch(() => Effect.succeed(store)))
|
||||
const vcs = yield* fs.realPath(store).pipe(Effect.orElseSucceed(() => store))
|
||||
return { path: path.join(vcs, "branch"), aliases: [".hg", vcs] }
|
||||
}
|
||||
}).pipe(
|
||||
|
|
|
|||
|
|
@ -622,7 +622,7 @@ export const layer = (options?: Options) =>
|
|||
const loadFromFile = options?.file
|
||||
? fs.readJson(options.file).pipe(
|
||||
Effect.map((input) => input as Record<string, SourceProvider>),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
)
|
||||
: Effect.succeed(undefined)
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ const oauth = (app: App.Info) =>
|
|||
},
|
||||
).pipe(
|
||||
Effect.map((user) => Option.getOrUndefined(decodeUser(user))?.endpoints?.api?.replace(/\/+$/, "")),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
Effect.map((apiEndpoint) =>
|
||||
Credential.OAuth.make({
|
||||
type: "oauth",
|
||||
|
|
@ -159,7 +159,7 @@ export const GithubCopilotPlugin = define({
|
|||
const load = Effect.fn("GithubCopilotPlugin.load")(function* () {
|
||||
const connection = yield* ctx.integration.connection.active("github-copilot")
|
||||
const credential = connection
|
||||
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.orElseSucceed(() => undefined))
|
||||
: undefined
|
||||
if (credential?.type !== "oauth") {
|
||||
loaded.baseURL = undefined
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export function make(origin = "http://127.0.0.1:11434", interval: Duration.Input
|
|||
)
|
||||
shows.set(model.model, { digest: model.digest, info })
|
||||
return { ...model, show: info }
|
||||
}).pipe(Effect.catch(() => Effect.succeed(undefined))),
|
||||
}).pipe(Effect.orElseSucceed(() => undefined)),
|
||||
{ concurrency: 4 },
|
||||
)
|
||||
const filtered = models.filter(
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ export const OpenAIPlugin = define({
|
|||
const load = Effect.fn("OpenAIPlugin.load")(function* () {
|
||||
const connection = yield* ctx.integration.connection.active("openai")
|
||||
const credential = connection
|
||||
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.orElseSucceed(() => undefined))
|
||||
: undefined
|
||||
chatgpt =
|
||||
credential?.type === "oauth" &&
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | Bus.Service | Scope
|
|||
const load = Effect.fn("OpencodePlugin.load")(function* () {
|
||||
const connection = yield* ctx.integration.connection.active("opencode")
|
||||
const credential = connection
|
||||
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.orElseSucceed(() => undefined))
|
||||
: undefined
|
||||
connected = connection !== undefined
|
||||
providers = credential
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ function poll(device: typeof Device.Type, app: App.Info): Effect.Effect<Token, u
|
|||
if (response.ok) return yield* decode(response, Token)
|
||||
const error = yield* Effect.promise(() => response.text()).pipe(
|
||||
Effect.map((body) => Option.getOrUndefined(decodeDeviceError(body))),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
)
|
||||
if (error?.error === "authorization_pending") {
|
||||
return yield* Effect.sleep(interval + pollingSafetyMargin).pipe(Effect.andThen(loop(interval)))
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export interface Resolved {
|
|||
export const root = Effect.fn("Project.root")(function* (fs: FSUtil.Interface, input: AbsolutePath) {
|
||||
return yield* fs.up({ targets: [".git", ".hg"], start: input, mode: "first" }).pipe(
|
||||
Effect.map((matches) => (matches[0] ? AbsolutePath.make(path.dirname(matches[0])) : undefined)),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ const layer = Layer.effect(
|
|||
return yield* fs.readFileString(path.join(dir, "opencode")).pipe(
|
||||
Effect.map((value) => value.trim()),
|
||||
Effect.map((value) => (value ? ID.make(value) : undefined)),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -202,7 +202,7 @@ const layer = Layer.effect(
|
|||
stdin: "ignore",
|
||||
}),
|
||||
)
|
||||
.pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
.pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (!result || result.exitCode !== 0) return undefined
|
||||
const node = result.stdout
|
||||
.toString("utf8")
|
||||
|
|
@ -216,7 +216,7 @@ const layer = Layer.effect(
|
|||
const hgDiscover = Effect.fnUntraced(function* (input: AbsolutePath) {
|
||||
const dotHg = yield* fs.up({ targets: [".hg"], start: input, mode: "first" }).pipe(
|
||||
Effect.map((matches) => matches[0]),
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
)
|
||||
if (!dotHg) return undefined
|
||||
const worktree = AbsolutePath.make(path.dirname(dotHg))
|
||||
|
|
@ -241,7 +241,7 @@ const layer = Layer.effect(
|
|||
? repo.worktree
|
||||
: yield* git.worktree.list(repo).pipe(
|
||||
Effect.map((items) => items.find((item) => item.kind === "main")?.directory ?? repo.worktree),
|
||||
Effect.catch(() => Effect.succeed(repo.worktree)),
|
||||
Effect.orElseSucceed(() => repo.worktree),
|
||||
)
|
||||
return yield* persist({
|
||||
previous,
|
||||
|
|
|
|||
|
|
@ -768,7 +768,7 @@ const layer = Layer.effect(
|
|||
const expanded =
|
||||
value === "~" ? global.home : value.startsWith("~/") ? path.join(global.home, value.slice(2)) : value
|
||||
const directory = AbsolutePath.make(path.resolve(current.location.directory, expanded))
|
||||
const info = yield* fs.stat(directory).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const info = yield* fs.stat(directory).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (!info) return yield* new DestinationNotFoundError({ directory })
|
||||
if (info.type !== "Directory") return yield* new DestinationNotDirectoryError({ directory })
|
||||
const project = yield* projects.resolve(directory)
|
||||
|
|
@ -787,7 +787,7 @@ const layer = Layer.effect(
|
|||
input.sessionID,
|
||||
Effect.gen(function* () {
|
||||
const latest = yield* result.get(input.sessionID)
|
||||
const source = yield* fs.stat(latest.location.directory).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const source = yield* fs.stat(latest.location.directory).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (!source || source.type !== "Directory") {
|
||||
const cancellations = (yield* SessionInbox.moveIDs(db, input.sessionID)).map(
|
||||
(item) => [SessionEvent.InboxCancelled, { sessionID: input.sessionID, inboxID: item.id }] as const,
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export const firstUserMessage = Effect.fn("SessionHistory.firstUserMessage")(fun
|
|||
.get()
|
||||
.pipe(Effect.orDie)
|
||||
if (!row) return undefined
|
||||
const message = yield* decodeMessageRow(row).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const message = yield* decodeMessageRow(row).pipe(Effect.orElseSucceed(() => undefined))
|
||||
return message?.type === "user" ? message : undefined
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ export const makeLayer = (connector: WebSocketConnector) =>
|
|||
}).pipe(
|
||||
Effect.andThen(metric("connect_failure")),
|
||||
Effect.andThen(metric("fallback")),
|
||||
Effect.asVoid,
|
||||
Effect.as(undefined),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const cleanup = Effect.fn("Shell.cleanup")(function* () {
|
|||
const directory = path.join(global.data, DIRECTORY)
|
||||
const projects = yield* fs.readDirectoryEntries(directory).pipe(
|
||||
Effect.map((entries) => entries.filter((entry) => entry.type === "directory")),
|
||||
Effect.catch(() => Effect.succeed([])),
|
||||
Effect.orElseSucceed(() => []),
|
||||
)
|
||||
const files = yield* Effect.forEach(
|
||||
projects,
|
||||
|
|
@ -90,7 +90,7 @@ export const cleanup = Effect.fn("Shell.cleanup")(function* () {
|
|||
: [],
|
||||
),
|
||||
),
|
||||
Effect.catch(() => Effect.succeed([])),
|
||||
Effect.orElseSucceed(() => []),
|
||||
),
|
||||
{ concurrency: 8 },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/To
|
|||
const cleanup = Effect.fn("ToolOutput.cleanup")(function* (fs: FSUtil.Interface, directory: string) {
|
||||
const entries = yield* fs.readDirectory(directory).pipe(
|
||||
Effect.map((entries) => entries.filter((entry) => /^tool_[0-9a-f]{12}/.test(entry))),
|
||||
Effect.catch(() => Effect.succeed([])),
|
||||
Effect.orElseSucceed(() => []),
|
||||
)
|
||||
yield* FileRetention.cleanup(
|
||||
fs,
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ export const Plugin = {
|
|||
.map((entry) => join(dirname(input), entry))
|
||||
.slice(0, 3),
|
||||
),
|
||||
Effect.catch(() => Effect.succeed([] as string[])),
|
||||
Effect.orElseSucceed(() => [] as string[]),
|
||||
)
|
||||
const message =
|
||||
suggestions.length === 0
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const layer = Layer.effect(
|
|||
const state = { info: impl ? yield* impl.info() : ({ branch: {} } satisfies Info) }
|
||||
|
||||
if (vcs && impl) {
|
||||
const store = yield* fs.realPath(vcs.store).pipe(Effect.catch(() => Effect.succeed(vcs.store)))
|
||||
const store = yield* fs.realPath(vcs.store).pipe(Effect.orElseSucceed(() => vcs.store))
|
||||
const isBranchMetadata =
|
||||
vcs.type === "git"
|
||||
? (file: string) => path.basename(file) === "HEAD" && FSUtil.contains(store, file)
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ function makeGit(proc: AppProcess.Interface) {
|
|||
truncated: result.stdoutTruncated || result.stderrTruncated,
|
||||
}
|
||||
},
|
||||
Effect.catch(() => Effect.succeed({ exitCode: 1, text: () => "", truncated: false })),
|
||||
Effect.orElseSucceed(() => ({ exitCode: 1, text: () => "", truncated: false })),
|
||||
)
|
||||
|
||||
const text = Effect.fnUntraced(function* (args: string[], opts: { cwd: string }) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe("ConfigWebSearchPlugin.Plugin", () => {
|
|||
yield* waitUntil(
|
||||
websearch.default().pipe(
|
||||
Effect.map((provider) => provider?.id === WebSearch.ID.make("test")),
|
||||
Effect.catch(() => Effect.succeed(false)),
|
||||
Effect.orElseSucceed(() => false),
|
||||
),
|
||||
)
|
||||
}).pipe(Effect.provide(Config.testLayer([configured(false)]))),
|
||||
|
|
|
|||
12
packages/core/test/effect-simplification-types.test.ts
Normal file
12
packages/core/test/effect-simplification-types.test.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { Effect } from "effect"
|
||||
|
||||
const source = Effect.succeed(1)
|
||||
const exactUndefined: Effect.Effect<undefined> = source.pipe(Effect.as(undefined))
|
||||
// @ts-expect-error Effect.asVoid widens the success type to void.
|
||||
const voidSuccess: Effect.Effect<undefined> = source.pipe(Effect.asVoid)
|
||||
|
||||
test("Effect.as preserves the exact undefined success type", () => {
|
||||
expect(Effect.runSync(exactUndefined)).toBeUndefined()
|
||||
expect(Effect.runSync(voidSuccess)).toBeUndefined()
|
||||
})
|
||||
|
|
@ -28,7 +28,7 @@ const checkMacosApp = Effect.fn("DesktopFiles.checkMacosApp")(function* (appName
|
|||
|
||||
return yield* Effect.tryPromise(() => execFilePromise("which", [appName])).pipe(
|
||||
Effect.as(true),
|
||||
Effect.catch(() => Effect.succeed(false)),
|
||||
Effect.orElseSucceed(() => false),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ const resolveWindowsAppPath = Effect.fn("DesktopFiles.resolveWindowsAppPath")(fu
|
|||
const fs = yield* FileSystem.FileSystem
|
||||
const path = yield* Path.Path
|
||||
const result = yield* Effect.tryPromise(() => execFilePromise("where", [appName])).pipe(
|
||||
Effect.catch(() => Effect.succeed(undefined)),
|
||||
Effect.orElseSucceed(() => undefined),
|
||||
)
|
||||
if (!result) return null
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ const resolveWindowsAppPath = Effect.fn("DesktopFiles.resolveWindowsAppPath")(fu
|
|||
for (const file of paths) {
|
||||
const dirs = [path.dirname(file), path.dirname(path.dirname(file)), path.dirname(path.dirname(path.dirname(file)))]
|
||||
for (const dir of dirs) {
|
||||
const entries = yield* fs.readDirectory(dir).pipe(Effect.catch(() => Effect.succeed([])))
|
||||
const entries = yield* fs.readDirectory(dir).pipe(Effect.orElseSucceed(() => []))
|
||||
for (const entry of entries) {
|
||||
const candidate = path.join(dir, entry)
|
||||
if (!hasExt(candidate, "exe")) continue
|
||||
|
|
@ -130,8 +130,5 @@ const resolveWindowsAppPath = Effect.fn("DesktopFiles.resolveWindowsAppPath")(fu
|
|||
})
|
||||
|
||||
function exists(fs: FileSystem.FileSystem, path: string) {
|
||||
return fs.access(path).pipe(
|
||||
Effect.as(true),
|
||||
Effect.catch(() => Effect.succeed(false)),
|
||||
)
|
||||
return fs.exists(path).pipe(Effect.orElseSucceed(() => false))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,10 +85,7 @@ function make(fs: FileSystem.FileSystem, path: Path.Path) {
|
|||
)
|
||||
}),
|
||||
revealPath: Effect.fn("DesktopFiles.revealPath")(function* (target: string) {
|
||||
const exists = yield* fs.stat(target).pipe(
|
||||
Effect.as(true),
|
||||
Effect.catch(() => Effect.succeed(false)),
|
||||
)
|
||||
const exists = yield* fs.exists(target).pipe(Effect.orElseSucceed(() => false))
|
||||
if (!exists) return false
|
||||
shell.showItemInFolder(target)
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ export const tail = Effect.fn("DesktopLogging.tail")(function* () {
|
|||
const contents = yield* fs.readFileString(path)
|
||||
const lines = contents.split("\n")
|
||||
return lines.slice(Math.max(0, lines.length - TAIL_LINES)).join("\n")
|
||||
}).pipe(Effect.catch(() => Effect.succeed("")))
|
||||
}).pipe(Effect.orElseSucceed(() => ""))
|
||||
})
|
||||
|
||||
function initRunDirectory(fs: FileSystem.FileSystem, path: Path.Path) {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export const cleanStages = Effect.fn("DesktopCli.cleanStages")(function* (binary
|
|||
Effect.fnUntraced(function* (entry) {
|
||||
const target = path.join(root, entry)
|
||||
if (target === current) return
|
||||
const stat = yield* fs.stat(target).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const stat = yield* fs.stat(target).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (stat?.type !== "Directory") return
|
||||
yield* fs
|
||||
.remove(target, { recursive: true, force: true })
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const cleanupStoreFiles = Effect.fn("Storage.cleanupStoreFiles")(function
|
|||
) {
|
||||
const fs = yield* FileSystem.FileSystem
|
||||
const path = yield* Path.Path
|
||||
const entries = yield* fs.readDirectory(userDataPath).pipe(Effect.catch(() => Effect.succeed([])))
|
||||
const entries = yield* fs.readDirectory(userDataPath).pipe(Effect.orElseSucceed(() => []))
|
||||
const candidates = (yield* Effect.forEach(
|
||||
entries,
|
||||
Effect.fnUntraced(function* (entry) {
|
||||
|
|
@ -27,7 +27,7 @@ export const cleanupStoreFiles = Effect.fn("Storage.cleanupStoreFiles")(function
|
|||
if (!kind) return
|
||||
|
||||
const file = path.join(userDataPath, entry)
|
||||
const stats = yield* fs.stat(file).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const stats = yield* fs.stat(file).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (stats?.type !== "File") return
|
||||
|
||||
return {
|
||||
|
|
@ -74,7 +74,7 @@ export const deleteStoreFileIfEmpty = Effect.fn("Storage.deleteStoreFileIfEmpty"
|
|||
const fs = yield* FileSystem.FileSystem
|
||||
const path = yield* Path.Path
|
||||
const file = path.join(userDataPath, name)
|
||||
const stats = yield* fs.stat(file).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const stats = yield* fs.stat(file).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (stats?.type !== "File") return false
|
||||
if (!(yield* isEmptyStore(file, stats.size))) return false
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ const isEmptyStore = Effect.fn("Storage.isEmptyStore")(function* (file: string,
|
|||
if (size > FileSystem.Size(EMPTY_STORE_MAX_BYTES)) return false
|
||||
|
||||
const fs = yield* FileSystem.FileSystem
|
||||
const raw = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
const raw = yield* fs.readFileString(file).pipe(Effect.orElseSucceed(() => undefined))
|
||||
if (raw === undefined) return false
|
||||
if (raw.trim() === "") return true
|
||||
|
||||
|
|
|
|||
|
|
@ -98,12 +98,12 @@ export const fileSystem = (
|
|||
const pathFor = (name: string) => cassettePath(directory, name)
|
||||
const walk = (current: string): Effect.Effect<ReadonlyArray<string>> =>
|
||||
Effect.gen(function* () {
|
||||
const entries = yield* fs.readDirectory(current).pipe(Effect.catch(() => Effect.succeed([] as string[])))
|
||||
const entries = yield* fs.readDirectory(current).pipe(Effect.orElseSucceed(() => [] as string[]))
|
||||
const nested = yield* Effect.forEach(entries, (entry) => {
|
||||
const full = path.join(current, entry)
|
||||
return fs.stat(full).pipe(
|
||||
Effect.flatMap((stat) => (stat.type === "Directory" ? walk(full) : Effect.succeed([full]))),
|
||||
Effect.catch(() => Effect.succeed([] as string[])),
|
||||
Effect.orElseSucceed(() => [] as string[]),
|
||||
)
|
||||
})
|
||||
return nested.flat()
|
||||
|
|
@ -144,11 +144,7 @@ export const fileSystem = (
|
|||
recorded.set(name, { interactions, findings: interactionFindings })
|
||||
}),
|
||||
),
|
||||
exists: (name) =>
|
||||
fs.access(pathFor(name)).pipe(
|
||||
Effect.as(true),
|
||||
Effect.catch(() => Effect.succeed(false)),
|
||||
),
|
||||
exists: (name) => fs.exists(pathFor(name)).pipe(Effect.orElseSucceed(() => false)),
|
||||
list: () =>
|
||||
walk(directory).pipe(
|
||||
Effect.map((files) =>
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ export namespace FSUtil {
|
|||
let current = start
|
||||
while (true) {
|
||||
const matches = yield* scan(pattern, { cwd: current, absolute: true, include: "file", dot: true }).pipe(
|
||||
Effect.catch(() => Effect.succeed([] as string[])),
|
||||
Effect.orElseSucceed(() => [] as string[]),
|
||||
)
|
||||
result.push(...matches)
|
||||
if (stop === current) break
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ const layer = Layer.effect(
|
|||
const binDir = path.join(dir, "node_modules", ".bin")
|
||||
|
||||
const pick = Effect.fnUntraced(function* () {
|
||||
const files = yield* fs.readDirectory(binDir).pipe(Effect.catch(() => Effect.succeed([] as string[])))
|
||||
const files = yield* fs.readDirectory(binDir).pipe(Effect.orElseSucceed(() => [] as string[]))
|
||||
|
||||
if (files.length === 0) return Option.none<string>()
|
||||
// Caller picked a specific bin (e.g. pyright exposes both `pyright` and
|
||||
|
|
|
|||
17
script/ast-grep/effect-simplifications/README.md
Normal file
17
script/ast-grep/effect-simplifications/README.md
Normal 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`.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
|
@ -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),
|
||||
),
|
||||
)}
|
||||
/>
|
||||
)
|
||||
|
|
@ -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[])),
|
||||
)
|
||||
|
|
@ -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) }))} />
|
||||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
|
|
@ -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) }))} />
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
4
script/ast-grep/effect-simplifications/sgconfig.yml
Normal file
4
script/ast-grep/effect-simplifications/sgconfig.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
ruleDirs:
|
||||
- rules
|
||||
testConfigs:
|
||||
- testDir: rule-tests
|
||||
Loading…
Add table
Add a link
Reference in a new issue