chore: add anti-slop Oxlint rules - #2163
Conversation
|
# Conflicts: # pnpm-lock.yaml
Remove the vendored anti-slop Effect plugin: no package in this repository uses Effect, so its rule had nothing to check. Add file-level exceptions for code that landed on main after the audit: the voice WebSocket transport test (module mocking) and the WebSockets callables dispatcher plus decorator scan (reflective dispatch on host objects), matching the existing observability proxy exceptions. Claude-Session: https://claude.ai/code/session_01LeAuE2FqjwnjJ6YBAaPMJx
There was a problem hiding this comment.
Devin Review found 4 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| function moduleMockCall(sourceCode: SourceCode, callee: ESTree.Expression): boolean { | ||
| if (!("property" in callee) || !("object" in callee) || !("computed" in callee)) return false; |
There was a problem hiding this comment.
🟡 Destructured module mocks bypass linting
After code destructures mock from vi or jest, moduleMockCall ignores the direct call. The banned module mock passes CI.
Prompt for agents
The no-module-mocking rule only recognizes member calls such as vi.mock(...). Track destructured or aliased references to the configured Vitest and Jest methods through scope definitions, then report their direct calls too. Add focused cases for destructuring and renamed destructuring from both vi and jest while preserving local-shadowing behavior.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if (!("property" in callee) || !("object" in callee) || !("computed" in callee)) return false; | ||
| if (!isGlobalReflect(sourceCode, callee.object)) return false; |
There was a problem hiding this comment.
🟡 Destructured reflection calls bypass linting
After code destructures apply or get from Reflect, isGlobalReflectMethodCall ignores the direct call. Both banned APIs pass CI.
Prompt for agents
The shared Reflect matcher only handles member expressions. Extend the enabled no-reflect-apply and no-reflect-get rules to resolve direct identifiers created by destructuring or aliasing the corresponding global Reflect method. Preserve shadowing checks and add cases for renamed destructuring and locally shadowed Reflect values.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const resolvesToUnknown = (type: ESTree.TSType, visited = new Set<string>()): boolean => { | ||
| if (type.type === "TSUnknownKeyword") return true; | ||
| if (type.type === "TSParenthesizedType") | ||
| return resolvesToUnknown(type.typeAnnotation, visited); | ||
| const name = referencedAliasName(type); |
There was a problem hiding this comment.
🟡 Unknown unions evade alias linting
For type Payload = unknown | string, resolvesToUnknown returns false although the alias equals unknown. The prohibited alias passes CI.
| const resolvesToUnknown = (type: ESTree.TSType, visited = new Set<string>()): boolean => { | |
| if (type.type === "TSUnknownKeyword") return true; | |
| if (type.type === "TSParenthesizedType") | |
| return resolvesToUnknown(type.typeAnnotation, visited); | |
| const name = referencedAliasName(type); | |
| const resolvesToUnknown = (type: ESTree.TSType, visited = new Set<string>()): boolean => { | |
| if (type.type === "TSUnknownKeyword") return true; | |
| if (type.type === "TSParenthesizedType") | |
| return resolvesToUnknown(type.typeAnnotation, visited); | |
| if (type.type === "TSUnionType") | |
| return type.types.some((member) => resolvesToUnknown(member, visited)); | |
| const name = referencedAliasName(type); |
Was this helpful? React with 👍 or 👎 to provide feedback.
| Program(node) { | ||
| aliases.clear(); | ||
| for (const statement of node.body) { | ||
| const declaration = | ||
| statement.type === "ExportNamedDeclaration" ? statement.declaration : statement; | ||
| if ( | ||
| declaration?.type === "TSTypeAliasDeclaration" && | ||
| (declaration.typeParameters === null || declaration.typeParameters === undefined) | ||
| ) { | ||
| aliases.set(declaration.id.name, declaration.typeAnnotation); | ||
| } | ||
| } |
There was a problem hiding this comment.
🟡 Block aliases evade parameter linting
When a block declares type Payload = object, Program never records it. Parameters using that prohibited broad alias pass CI.
Prompt for agents
The no-object-parameters rule builds one map from top-level Program statements, so aliases declared inside functions, blocks, or namespaces are invisible. Resolve type alias declarations according to lexical scope, including shadowing by nested aliases and type parameters, before checking each parameter. Add focused tests for block-local, function-local, namespace, shadowed, and recursive aliases.
Was this helpful? React with 👍 or 👎 to provide feedback.
agents
@cloudflare/ai-chat
@cloudflare/codemode
hono-agents
@cloudflare/shell
@cloudflare/think
@cloudflare/voice
@cloudflare/worker-bundler
commit: |
Summary
tools/oxlint/anti-slopoxlintand@oxlint/plugins1.80.0 releases and register the plugin in the normal root lint configurationtools/oxlint/anti-slop/README.mdThe upstream Effect-specific plugin is not vendored. No package in this repository uses Effect, so its rule would have nothing to check.
A strict initial scan found 13,205 existing anti-slop findings across 824 files. This PR enables the rules the repository can adopt cleanly now. The remaining rules stay visible as
offrather than disappearing from the configuration, so they can be migrated package by package.No changeset is included because this changes repository tooling and internal implementation details without changing package behavior or public APIs.
Verification
pnpm run buildpnpm run check(sherif, exports, oxfmt, oxlint, all 121 TypeScript projects) after mergingmainpnpm exec nx affected -t test, covering 22 affected projects