slop-police 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -24
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# slop-police
|
|
2
2
|
|
|
3
|
-
A fast, type-aware linter for TypeScript that catches the dead code and needless indirection AI coding agents tend to leave behind: methods nobody calls, object keys nobody reads, getters and
|
|
3
|
+
A fast, type-aware linter for TypeScript that catches the dead code and needless indirection AI coding agents tend to leave behind: methods nobody calls, object keys nobody reads, getters, aliases and wrappers that only rename or forward something, helpers copied instead of reused, comments that only promise future work, and errors swallowed or flattened into strings.
|
|
4
4
|
|
|
5
5
|
It runs the TypeScript 7 compiler in-process, so rules resolve symbols the same way `tsc` does, across all of your tsconfig projects at once.
|
|
6
6
|
|
|
@@ -31,7 +31,12 @@ slop-police reads `slop-police.json` from the current directory:
|
|
|
31
31
|
"no-identity-type-alias": "warn",
|
|
32
32
|
"no-pass-through-getter": "error",
|
|
33
33
|
"no-redundant-value-alias": "error",
|
|
34
|
-
"no-single-string-cn": "off"
|
|
34
|
+
"no-single-string-cn": "off",
|
|
35
|
+
"no-pass-through-wrapper": "error",
|
|
36
|
+
"no-duplicate-function": "warn",
|
|
37
|
+
"no-placeholder-comment": "error",
|
|
38
|
+
"no-promise-default-fallback": "error",
|
|
39
|
+
"no-stringified-unknown-error": "warn"
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
```
|
|
@@ -91,6 +96,24 @@ class Store {
|
|
|
91
96
|
}
|
|
92
97
|
```
|
|
93
98
|
|
|
99
|
+
### no-pass-through-wrapper
|
|
100
|
+
|
|
101
|
+
A function or method whose body only returns a call (awaited or not) to a named function, passing its own parameters on as they are.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
function loadUser(id: string) { // Function loadUser only forwards to api.loadUser
|
|
105
|
+
return api.loadUser(id);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
It uses types to skip wrappers that do something:
|
|
110
|
+
|
|
111
|
+
- the shape is required: a method of a typed object literal (a `Proxy` handler, a Worker's `fetch`), a method that implements or overrides an inherited member, or a function assigned to a variable with a declared type. An override that only calls `super`'s method of the same name is still reported.
|
|
112
|
+
- the wrapper adds types: type parameters, overloads, or parameter and return types declared differently from the callee's, like `t(key: TranslationKey)` around `translate(key: string)`.
|
|
113
|
+
- it delegates to a private or protected member, like `get(key) { return this.items.get(key); }`.
|
|
114
|
+
|
|
115
|
+
Calls on computed values (`getDay(date).getTime()`, `/^\s*$/.test(text)`) aren't forwarding, so they don't count.
|
|
116
|
+
|
|
94
117
|
### no-redundant-value-alias
|
|
95
118
|
|
|
96
119
|
A local `const` that only renames another `const`, when using the original directly means the same thing.
|
|
@@ -111,47 +134,76 @@ function render() {
|
|
|
111
134
|
<div className={cn("flex gap-2")} /> // Use the class string directly instead of wrapping it in cn()
|
|
112
135
|
```
|
|
113
136
|
|
|
114
|
-
|
|
137
|
+
### no-placeholder-comment
|
|
115
138
|
|
|
116
|
-
A
|
|
139
|
+
A comment that only promises future work instead of describing the code: "add more validation", "handle more cases", "can be extended in the future", "extend this logic", "customize this behavior", "future enhancement", "implement … here". Ordinary TODOs don't match.
|
|
117
140
|
|
|
118
141
|
```ts
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
|
|
142
|
+
function parse(input: string) {
|
|
143
|
+
// Add more validation here
|
|
144
|
+
return JSON.parse(input);
|
|
122
145
|
}
|
|
146
|
+
```
|
|
123
147
|
|
|
124
|
-
|
|
125
|
-
|
|
148
|
+
### no-duplicate-function
|
|
149
|
+
|
|
150
|
+
A function whose body has the same shape as functions in at least two other files: a helper that was rewritten instead of reused. Shapes ignore the names of parameters and locals and the values of literals, but not the names of what the body calls or reads. Bodies with fewer than 2 statements or 16 syntax nodes, and functions in test files, don't count. Option: `minFiles` (default `3`, at least `2`), the number of files a shape has to appear in.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
// users.ts, posts.ts and teams.ts each have:
|
|
154
|
+
export async function loadUsers(id: string) {
|
|
155
|
+
const response = await fetchJson("/users/" + id);
|
|
156
|
+
if (!response.ok) throw new Error("failed to load users");
|
|
157
|
+
return response.data;
|
|
158
|
+
}
|
|
159
|
+
// Function loadUsers has the same body shape as loadPosts (posts.ts:1), loadTeams (teams.ts:1)
|
|
126
160
|
```
|
|
127
161
|
|
|
128
|
-
|
|
162
|
+
### no-promise-default-fallback
|
|
129
163
|
|
|
130
|
-
|
|
164
|
+
A promise `.catch()` handler that hides the rejection: an empty handler, or one that only returns `null`, `undefined`, `false`, `0`, `""`, `[]` or `{}`, optionally after `console.*` or `logger.*` calls. Only `.catch` on promises and other thenables counts.
|
|
131
165
|
|
|
132
|
-
|
|
166
|
+
```ts
|
|
167
|
+
const config = await fetchConfig().catch(() => null); // Promise rejection is replaced with null
|
|
168
|
+
navigator.clipboard.writeText(text).catch(() => {}); // Promise rejection is swallowed by an empty catch handler
|
|
169
|
+
const enabled = await fetchFlag().catch((error) => { // Promise rejection is logged and replaced with false
|
|
170
|
+
console.error("flag load failed", error);
|
|
171
|
+
return false;
|
|
172
|
+
});
|
|
173
|
+
```
|
|
133
174
|
|
|
134
|
-
|
|
175
|
+
### no-stringified-unknown-error
|
|
135
176
|
|
|
136
|
-
|
|
177
|
+
`error instanceof Error ? error.message : String(error)` (in either order), which flattens a caught value into a string and drops its type, stack and cause. A plain `instanceof Error` check counts when the variable is named `error`, `err`, `e` or `cause`; an `||` of `instanceof` checks counts for any name.
|
|
137
178
|
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
|
|
179
|
+
```ts
|
|
180
|
+
try {
|
|
181
|
+
await sendMessage(chatId, text);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) }; // Caught value error is flattened into a string
|
|
184
|
+
}
|
|
141
185
|
```
|
|
142
186
|
|
|
143
|
-
|
|
187
|
+
## Ignoring findings
|
|
144
188
|
|
|
145
|
-
|
|
189
|
+
A `// slop-police-ignore` comment directly above a statement, class member or object property silences findings in it, including everything nested inside. List rule names to silence only those, and add a reason after `--`:
|
|
146
190
|
|
|
147
|
-
|
|
191
|
+
```ts
|
|
192
|
+
class Store {
|
|
193
|
+
// slop-police-ignore unused-methods -- called from the devtools console
|
|
194
|
+
debug() {}
|
|
195
|
+
}
|
|
148
196
|
|
|
149
|
-
|
|
150
|
-
|
|
197
|
+
// slop-police-ignore
|
|
198
|
+
export const legacyLinks = { a: "/a", b: "/b" };
|
|
151
199
|
```
|
|
152
200
|
|
|
153
|
-
|
|
201
|
+
For `no-placeholder-comment`, put the ignore comment on the line directly above the reported comment.
|
|
202
|
+
|
|
203
|
+
## Requirements
|
|
204
|
+
|
|
205
|
+
macOS or Linux on arm64 or x64 (on Windows, use WSL). The linter bundles its own TypeScript 7 compiler, so your tsconfigs need to work with TypeScript 7.
|
|
154
206
|
|
|
155
207
|
## License
|
|
156
208
|
|
|
157
|
-
MIT
|
|
209
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slop-police",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Type-aware TypeScript linter for the dead code and needless indirection AI agents leave behind",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"node": ">=18"
|
|
29
29
|
},
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"slop-police-darwin-arm64": "0.
|
|
32
|
-
"slop-police-darwin-x64": "0.
|
|
33
|
-
"slop-police-linux-arm64": "0.
|
|
34
|
-
"slop-police-linux-x64": "0.
|
|
31
|
+
"slop-police-darwin-arm64": "0.2.0",
|
|
32
|
+
"slop-police-darwin-x64": "0.2.0",
|
|
33
|
+
"slop-police-linux-arm64": "0.2.0",
|
|
34
|
+
"slop-police-linux-x64": "0.2.0"
|
|
35
35
|
}
|
|
36
36
|
}
|