slop-police 0.3.7 → 0.3.8

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.
Files changed (2) hide show
  1. package/README.md +90 -19
  2. package/package.json +5 -5
package/README.md CHANGED
@@ -6,50 +6,92 @@
6
6
  A type-aware TypeScript linter for the dead code and needless indirection AI agents leave behind.
7
7
  </p>
8
8
 
9
+ ```ts
10
+ import { api } from "./api";
11
+
12
+ export type User = { id: string; name: string };
13
+
14
+ export function fetchUser(id: string) {
15
+ return api.getUser(id);
16
+ }
17
+
18
+ export class UserCache {
19
+ private users = new Map<string, User>();
20
+
21
+ async get(id: string) {
22
+ const user = this.users.get(id) ?? (await fetchUser(id));
23
+ const cached = user;
24
+ this.users.set(id, cached);
25
+ return cached;
26
+ }
27
+
28
+ has(id: string) {
29
+ return this.users.has(id);
30
+ }
31
+ }
32
+ ```
33
+
34
+ slop-police isn't:
35
+
36
+ ```
37
+ user-cache.ts:3:13 error unused-exports Export User is only used in its own file
38
+ user-cache.ts:5:17 error no-pass-through-wrapper Function fetchUser only forwards to api.getUser
39
+ user-cache.ts:5:17 error unused-exports Export fetchUser is only used in its own file
40
+ user-cache.ts:14:11 error no-redundant-value-alias Local alias cached only renames user; use the original binding directly
41
+ user-cache.ts:19:3 error unused-methods Method UserCache.has is never used
42
+
43
+ 5 errors, 0 warnings
44
+ ```
45
+
9
46
  ```sh
10
- pnpm add slop-police
47
+ pnpm add -D slop-police
11
48
  # or
12
- npm i slop-police
49
+ npm i -D slop-police
13
50
  ```
14
51
 
15
- Run it with `pnpm exec slop-police` or `npx slop-police`.
52
+ Add a [`slop-police.json`](#configuration) and run `npx slop-police` (or `pnpm exec slop-police`).
16
53
 
17
- Example output:
54
+ ## Instead of knip and jscpd
18
55
 
19
- ```
20
- deck-store.ts:42:3 error unused-methods Method DeckStore.reset is never used
21
- links.ts:30:3 error unused-object-keys links.privacyPolicy is never read
56
+ | | slop-police | knip | jscpd |
57
+ | ------------------------------------------ | :---------: | :--: | :---: |
58
+ | Unused exports and files | ✓ | ✓ | |
59
+ | Unused enum members | ✓ | ✓ | |
60
+ | Unused class methods | ✓ | | |
61
+ | Unused object keys | ✓ | | |
62
+ | Copy-pasted code | ✓ | | ✓ |
63
+ | Helpers rewritten instead of reused | ✓ | | |
64
+ | Pass-through wrappers, getters and aliases | ✓ | | |
65
+ | Placeholder comments, flattened errors | ✓ | | |
66
+
67
+ ## CI
22
68
 
23
- 2 errors, 0 warnings
69
+ Make CI fail when something slips through
70
+
71
+ ```yaml
72
+ - run: npx slop-police
24
73
  ```
25
74
 
26
75
  ## Configuration
27
76
 
28
- slop-police reads `slop-police.json` from the current directory:
77
+ Put a `slop-police.json` in the directory you run it from:
29
78
 
30
79
  ```json
31
80
  {
32
81
  "projects": ["packages/api/tsconfig.json", "packages/frontend/tsconfig.app.json"],
33
82
  "ignore": ["**/.next/**"],
34
- "testFiles": ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
35
- "rules": {
36
- "unused-methods": ["error", { "includePrivate": true }],
37
- "no-identity-type-alias": "warn",
38
- "no-single-string-cn": "off"
39
- }
83
+ "testFiles": ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"]
40
84
  }
41
85
  ```
42
86
 
43
87
  - `projects`: tsconfig files to analyze. Usages are counted across all of them, so a method used only by another package still counts as used.
44
88
  - `ignore`: glob patterns (relative to the config) for files to skip entirely. Files that `.gitignore` ignores are skipped too.
45
89
  - `testFiles`: glob patterns for tests. Usages inside tests count, but declarations inside tests are not reported.
46
- - `rules`: every built-in rule runs as an error with its default options. List a rule here to change that: `"error"`, `"warn"` or `"off"`, or `[severity, options]`. Plugin rules run only when they're listed.
90
+ - `rules`: you usually don't need it, since every built-in rule is already on as an error. It's for rule options, like the stricter `unused-exports` setup below, and for turning on plugin rules. A rule takes `"error"`, `"warn"`, `"off"` or `[severity, options]`.
47
91
  - `plugins`: modules with [custom rules](#custom-rules), as paths relative to the config or package names.
48
92
 
49
93
  Exit code is `1` when there are errors, `2` for an invalid config, `0` otherwise.
50
94
 
51
- slop-police needs your dependencies installed: without their types it can't tell where a method or key is used. When imports in the analyzed files don't resolve (and no `declare module` covers them), it lists them and exits with `1` without linting. Side-effect imports like `import "./styles.css"` don't count.
52
-
53
95
  ## Rules
54
96
 
55
97
  ### unused-methods
@@ -64,7 +106,7 @@ class Store {
64
106
  new Store().load();
65
107
  ```
66
108
 
67
- A member also counts as used when it implements an interface member that is used or that is declared outside the analyzed code (a library calls it), when it's named in `Pick<Class, "name">`, or when an instance is passed to a JSX prop whose type requires it. Options: `includePrivate` and `includeProtected` (both `false` by default).
109
+ It knows the ways a method gets called without being named: it implements an interface member that's used, or one from a library (the library calls it), it's picked with `Pick<Class, "name">`, or an instance goes to a JSX prop whose type needs it. Private and protected members are skipped unless you turn on `includePrivate` or `includeProtected`.
68
110
 
69
111
  ### unused-object-keys
70
112
 
@@ -78,6 +120,35 @@ export const links = {
78
120
  open(links.docs);
79
121
  ```
80
122
 
123
+ ### unused-exports
124
+
125
+ Agents love to `export` things just in case. This rule finds exports used only in their own file (just drop `export`) and exports nothing uses at all. Imports and re-exports count as uses, and so do uses in tests and ignored files. Files that nothing imports are entry points (`main.tsx`, a framework route, a config file), so their exports are left alone. A dynamic import (`import("./page")`) or a namespace used as a value (`Object.keys(ns)`) counts as using everything in the module.
126
+
127
+ ```ts
128
+ // lib.ts, imported by main.ts as import { parse } from "./lib"
129
+ export type Options = { strict: boolean }; // Export Options is only used in its own file
130
+ export function parse(text: string, options?: Options) {}
131
+ export function format() {} // Export format is never used
132
+ ```
133
+
134
+ If you want to be extra strict, list your entry points in the `entry` option. slop-police then also finds whole files that nothing imports from them or from tests. Code used only by such dead files counts as unused too.
135
+
136
+ ```json
137
+ "unused-exports": ["error", { "entry": ["src/main.tsx", "src/routes/**/*.tsx", "vite.config.ts"] }]
138
+ ```
139
+
140
+ ### unused-enum-members
141
+
142
+ Enum members nothing uses. When the enum is used as a whole (`Object.values(Status)`, `keyof typeof Status`, passed to a function), any member could be in play, so the enum is skipped.
143
+
144
+ ```ts
145
+ enum Status {
146
+ Active,
147
+ Archived, // Enum member Status.Archived is never used
148
+ }
149
+ const isActive = (status: Status) => status === Status.Active;
150
+ ```
151
+
81
152
  ### no-identity-type-alias
82
153
 
83
154
  A type alias that only renames another type.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slop-police",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Type-aware TypeScript linter for the dead code and needless indirection AI agents leave behind",
5
5
  "keywords": [
6
6
  "typescript",
@@ -41,9 +41,9 @@
41
41
  "node": ">=22.18"
42
42
  },
43
43
  "optionalDependencies": {
44
- "slop-police-darwin-arm64": "0.3.7",
45
- "slop-police-darwin-x64": "0.3.7",
46
- "slop-police-linux-arm64": "0.3.7",
47
- "slop-police-linux-x64": "0.3.7"
44
+ "slop-police-darwin-arm64": "0.3.8",
45
+ "slop-police-darwin-x64": "0.3.8",
46
+ "slop-police-linux-arm64": "0.3.8",
47
+ "slop-police-linux-x64": "0.3.8"
48
48
  }
49
49
  }