knip 1.6.0 → 1.7.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 CHANGED
@@ -8,7 +8,7 @@ export const myVar = true;
8
8
  ```
9
9
 
10
10
  ESLint handles files in isolation, so it does not know whether `myVar` is actually used somewhere else. Knip lints the
11
- project as a whole, and finds unused exports, files and dependencies
11
+ project as a whole, and finds unused exports, files and dependencies.
12
12
 
13
13
  It's only human to forget removing things that you no longer use. But how do you find out? Where to even start finding
14
14
  things that can be removed?
@@ -26,25 +26,22 @@ The dots don't connect themselves. This is where Knip comes in:
26
26
  - [x] Features multiple [reporters][2] and supports [custom reporters][3]
27
27
  - [x] Run Knip as part of your CI environment to detect issues and prevent regressions
28
28
 
29
- Knip shines in both small and large projects. A comparison with similar tools answers the question [why another unused
30
- file/dependency/export finder?][4]
29
+ Knip shines in both small and large projects. It's a fresh take on keeping your projects clean & tidy!
31
30
 
32
- Knip is a fresh take on keeping your projects clean & tidy!
33
-
34
- [![An orange cow with scissors, Van Gogh style][6]][5] <sup>_“An orange cow with scissors, Van Gogh style” - generated
31
+ [![An orange cow with scissors, Van Gogh style][5]][4] <sup>_“An orange cow with scissors, Van Gogh style” - generated
35
32
  with OpenAI_</sup>
36
33
 
37
34
  ## Migrating to v1.0.0
38
35
 
39
- When coming from version v0.13.3 or before, please see [migration to v1][7].
36
+ When coming from version v0.13.3 or before, please see [migration to v1][6].
40
37
 
41
38
  ## Issues
42
39
 
43
- Please report any false positives by [opening an issue in this repo][8]. Bonus points for linking to a public repository
44
- using Knip, or even opening a pull request with a directory and example files in `test/fixtures`. Correctness and bug
45
- fixes have priority over performance and new features.
40
+ Are you seeing false positives? Please report them by [opening an issue in this repo][7]. Bonus points for linking to a
41
+ public repository using Knip, or even opening a pull request with a directory and example files in `test/fixtures`.
42
+ Correctness and bug fixes have priority over performance and new features.
46
43
 
47
- Also see the [FAQ][9].
44
+ Also see the [FAQ][8].
48
45
 
49
46
  ## Installation
50
47
 
@@ -69,7 +66,7 @@ with a configuration file (or a `knip` property in `package.json`). Let's name t
69
66
  The `entry` files target the starting point(s) to resolve the rest of the imported code. The `project` files should
70
67
  contain all files to match against the files resolved from the entry files, including potentially unused files.
71
68
 
72
- Then run the checks with `npx knip`, or add a script to `package.json`:
69
+ Then run the checks with `npx knip`. Or first add this script to `package.json`:
73
70
 
74
71
  ```json
75
72
  {
@@ -79,7 +76,8 @@ Then run the checks with `npx knip`, or add a script to `package.json`:
79
76
  }
80
77
  ```
81
78
 
82
- Use `npm run knip` to analyze the project and output unused files, dependencies and exports.
79
+ Use `npm run knip` to analyze the project and output unused files, dependencies and exports. Knip works just fine with
80
+ `yarn` or `pnpm` as well.
83
81
 
84
82
  ## Command-line options
85
83
 
@@ -146,6 +144,15 @@ The report contains the following types of issues:
146
144
  - **Unused exported class members**: did not find references to this member of the exported class
147
145
  - **Duplicate exports**: the same thing is exported more than once
148
146
 
147
+ When an issue type has zero issues, it is not shown.
148
+
149
+ _(1)_ This includes imports that could not be resolved.
150
+
151
+ _(2)_ The variable or type is not referenced directly, and has become a member of a namespace. Knip can't find a
152
+ reference to it, so you can _probably_ remove it.
153
+
154
+ ### Output filters
155
+
149
156
  You can `--include` or `--exclude` any of the types to slice & dice the report to your needs. Alternatively, they can be
150
157
  added to the configuration (e.g. `"exclude": ["dependencies"]`).
151
158
 
@@ -164,18 +171,20 @@ Use `--exclude` to ignore reports you're not interested in:
164
171
  Use `--dependencies` or `--exports` as shortcuts to combine groups of related types.
165
172
 
166
173
  Still not happy with the results? Getting too much output/false positives? The [FAQ][9] may be useful. Feel free to open
167
- an issue and I'm happy to look into it.
174
+ an issue and I'm happy to look into it. Also see the next section on how to [ignore][10] certain false positives:
168
175
 
169
- ---
176
+ ## Ignore
170
177
 
171
- _(1)_ This includes dependencies that could not be resolved. For instance, what does `unresolved/dir/module` mean?
178
+ There are a few ways to tell Knip to ignore certain packages, binaries, dependencies and workspaces. Some examples:
172
179
 
173
- - It might target a missing `unresolved` package in `node_modules/unresolved`.
174
- - It might incorrectly target a local module that should have a relative path.
175
- - It does not match any `paths` entry in `tsconfig.json#compilerOptions`.
176
-
177
- _(2)_ The variable or type is not referenced directly, and has become a member of a namespace. That's why Knip is not
178
- sure whether this export can be removed, so please look into it.
180
+ ```json
181
+ {
182
+ "ignore": ["**/*.d.ts", "**/fixtures"],
183
+ "ignoreBinaries": ["zip", "docker-compose"],
184
+ "ignoreDependencies": ["hidden-package"],
185
+ "ignoreWorkspaces": ["packages/deno-lib"]
186
+ }
187
+ ```
179
188
 
180
189
  ## Now what?
181
190
 
@@ -218,7 +227,7 @@ analysis.
218
227
  Extra "workspaces" not configured as a workspace in the root `package.json` can be configured as well, Knip is happy to
219
228
  analyze unused dependencies and exports from any directory with a `package.json`.
220
229
 
221
- Here's a small output example when running Knip in a workspace:
230
+ Here's some example output when running Knip in a workspace:
222
231
 
223
232
  <img src="./assets/screenshot-workspaces.png" alt="example output in workspaces" width="578">
224
233
 
@@ -226,7 +235,7 @@ Here's a small output example when running Knip in a workspace:
226
235
 
227
236
  Knip contains a growing list of plugins:
228
237
 
229
- - [Babel][10]
238
+ - [Babel][9]
230
239
  - [Capacitor][11]
231
240
  - [Changesets][12]
232
241
  - [commitlint][13]
@@ -257,9 +266,9 @@ Knip contains a growing list of plugins:
257
266
  - [Vitest][38]
258
267
  - [Webpack][39]
259
268
 
260
- Plugins are automatically activated, no need to enable anything. Each plugin is automatically enabled based on simple
261
- heuristics. Most of them check whether one or one of a few (dev) dependencies are listed in `package.json`. Once
262
- enabled, they add a set of configuration and/or entry files for Knip to analyze. These defaults can be overriden.
269
+ Plugins are automatically activated. Each plugin is automatically enabled based on simple heuristics. Most of them check
270
+ whether one or one of a few (dev) dependencies are listed in `package.json`. Once enabled, they add a set of
271
+ configuration and/or entry files for Knip to analyze. These defaults can be overriden.
263
272
 
264
273
  Most plugins use one or both of the following file types:
265
274
 
@@ -270,7 +279,7 @@ See each plugin's documentation for its default values.
270
279
 
271
280
  ### `config`
272
281
 
273
- Plugins may include `config` files. They are parsed by the plugin's custom dependency resolvers. Here are some examples
282
+ Plugins may include `config` files. They are parsed by the plugin's custom dependency resolver. Here are some examples
274
283
  to get an idea of how they work and why they are needed:
275
284
 
276
285
  - The `eslint` plugin tells Knip that the `"prettier"` entry in the array of `plugins` means that the
@@ -429,10 +438,11 @@ all of this, why not collect the various issues in one go?
429
438
  The structure and configuration of projects and their dependencies vary wildly, and no matter how well-balanced,
430
439
  defaults only get you so far. Some implementations and some tools out there have smart or unconventional ways to import
431
440
  code, making things more complicated. That's why Knip tends to require more configuration in larger projects, based on
432
- how many dependencies are used and how much the project diverges from the defaults.
441
+ how many dependencies are used and how much the configuration in the project diverges from the defaults.
433
442
 
434
- One important goal of Knip is to minimize the amount of configuration necessary. So when there are feasible ways to
435
- infer things automatically, reducing the amount of configuration, please open an issue.
443
+ One important goal of Knip is to minimize the amount of configuration necessary. When you false positives are reported
444
+ and you think there are feasible ways to infer things automatically, reducing the amount of configuration, please open
445
+ an issue.
436
446
 
437
447
  ### How do I handle too many output/false positives?
438
448
 
@@ -457,17 +467,21 @@ When the dependencies don't have a Knip plugin yet, please file an issue or [cre
457
467
 
458
468
  When the project is a library and the exports are meant to be used by consumers of the library, there are two options:
459
469
 
460
- 1. By default, unused exports of `entry` files are not reported, so you can add the containing file to it.
470
+ 1. By default, unused exports of `entry` files are not reported. You could re-export from an existing entry file, or
471
+ add the containing file to the `entry` array in the configuration.
461
472
  2. The exported values or types can be marked [using the JSDoc `@public` tag][47].
462
473
 
463
474
  ### How to start using Knip in CI while having too many issues to sort out?
464
475
 
465
476
  Eventually this type of QA only really works when it's tied to an automated workflow. But with too many issues to
466
- resolve this might not be feasible right away, especially in existing larger codebase. Here are a few options:
477
+ resolve this might not be feasible right away, especially in existing larger codebase. Here are a few options that may
478
+ help:
467
479
 
468
480
  - Use `--no-exit-code` for exit code 0 in CI.
469
- - Use `--include` (or `--exclude`) to report only the issue types don't have errors.
481
+ - Use `--include` (or `--exclude`) to report only the issue types that have little or no errors.
482
+ - Use a separate `--dependencies` and/or `--exports` Knip command.
470
483
  - Use `ignore` (for files and directories) and `ignoreDependencies` to filter out some problematic areas.
484
+ - Limit the number of workspaces configured to analyze in `knip.json`.
471
485
 
472
486
  All of this is hiding problems, so please make sure to plan for fixing them and/or open issues here for false positives.
473
487
 
@@ -543,13 +557,13 @@ for the job. I'm motivated to make knip perfectly suited for the job of cutting
543
557
  [1]: #plugins
544
558
  [2]: #reporters
545
559
  [3]: #custom-reporters
546
- [4]: #really-another-unused-filedependencyexport-finder
547
- [5]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
548
- [6]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
549
- [7]: ./docs/migration-to-v1.md
550
- [8]: https://github.com/webpro/knip/issues
551
- [9]: #faq
552
- [10]: ./src/plugins/babel
560
+ [4]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
561
+ [5]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
562
+ [6]: ./docs/migration-to-v1.md
563
+ [7]: https://github.com/webpro/knip/issues
564
+ [8]: #faq
565
+ [9]: ./src/plugins/babel
566
+ [10]: #ignore
553
567
  [11]: ./src/plugins/capacitor
554
568
  [12]: ./src/plugins/changesets
555
569
  [13]: ./src/plugins/commitlint
package/dist/constants.js CHANGED
@@ -7,7 +7,23 @@ export const DEFAULT_WORKSPACE_CONFIG = {
7
7
  ignore: [],
8
8
  };
9
9
  export const TEST_FILE_PATTERNS = ['**/*.{test,spec}.{js,jsx,ts,tsx}', '**/__tests__/**/*.{js,jsx,ts,tsx}'];
10
- export const IGNORED_GLOBAL_BINARIES = ['npm', 'npx', 'node', 'yarn', 'pnpm', 'deno', 'git'];
10
+ export const IGNORED_GLOBAL_BINARIES = [
11
+ 'deno',
12
+ 'git',
13
+ 'node',
14
+ 'npm',
15
+ 'npx',
16
+ 'pnpm',
17
+ 'yarn',
18
+ 'cd',
19
+ 'cp',
20
+ 'echo',
21
+ 'exit',
22
+ 'mkdir',
23
+ 'mv',
24
+ 'rm',
25
+ 'sudo',
26
+ ];
11
27
  export const IGNORE_DEFINITELY_TYPED = ['node'];
12
28
  export const ISSUE_TYPES = [
13
29
  'files',
@@ -1,5 +1,5 @@
1
- import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';
2
1
  import type { BabelConfig } from './types.js';
2
+ import type { IsPluginEnabledCallback, GenericPluginCallback } from '../../types/plugins.js';
3
3
  export declare const NAME = "Babel";
4
4
  export declare const ENABLERS: RegExp[];
5
5
  export declare const isEnabled: IsPluginEnabledCallback;
@@ -6,7 +6,7 @@ import { timerify } from '../../util/performance.js';
6
6
  export const NAME = 'GitHub Actions';
7
7
  export const ENABLERS = 'This plugin is enabled when a `.yml` file is found in the `.github/workflows` folder.';
8
8
  export const isEnabled = async ({ cwd }) => Boolean(await _firstGlob({ cwd, patterns: ['.github/workflows/*.yml'] }));
9
- export const CONFIG_FILE_PATTERNS = ['.github/workflows/*.yml'];
9
+ export const CONFIG_FILE_PATTERNS = ['.github/workflows/*.yml', '.github/**/action.{yml,yaml}'];
10
10
  const findGithubActionsDependencies = async (configFilePath, { manifest, rootConfig }) => {
11
11
  const config = await _load(configFilePath);
12
12
  if (!config)
@@ -1,4 +1,5 @@
1
1
  import { compact } from '../../util/array.js';
2
+ import { getBinariesFromScripts } from '../../util/binaries/index.js';
2
3
  import { _load } from '../../util/loader.js';
3
4
  import { timerify } from '../../util/performance.js';
4
5
  import { hasDependency } from '../../util/plugin.js';
@@ -6,10 +7,19 @@ export const NAME = 'Nx';
6
7
  export const ENABLERS = [/^@nrwl\//];
7
8
  export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
8
9
  export const CONFIG_FILE_PATTERNS = ['{apps,libs}/**/project.json'];
9
- const findNxDependencies = async (configFilePath) => {
10
+ const findNxDependencies = async (configFilePath, { manifest }) => {
10
11
  const config = await _load(configFilePath);
11
- const { targets } = config;
12
- const executors = targets ? Object.values(targets).map(target => target?.executor) : [];
13
- return compact(executors.filter(executor => executor && !executor.startsWith('.')).map(executor => executor?.split(':')[0]));
12
+ if (!config)
13
+ return [];
14
+ const targets = config.targets ? Object.values(config.targets) : [];
15
+ const executors = compact(targets
16
+ .map(target => target?.executor)
17
+ .filter(executor => executor && !executor.startsWith('.'))
18
+ .map(executor => executor?.split(':')[0]));
19
+ const scripts = compact(targets
20
+ .filter(target => target.executor === 'nx:run-commands')
21
+ .flatMap(target => (target.options?.commands ?? target.options?.command ? [target.options.command] : [])));
22
+ const binaries = getBinariesFromScripts(scripts, { manifest, knownGlobalsOnly: true });
23
+ return [...executors, ...binaries];
14
24
  };
15
25
  export const findDependencies = timerify(findNxDependencies);
@@ -0,0 +1,11 @@
1
+ export interface NxProjectConfiguration {
2
+ targets?: {
3
+ [targetName: string]: {
4
+ executor?: string;
5
+ options?: {
6
+ command?: string;
7
+ commands?: string[];
8
+ };
9
+ };
10
+ };
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
+ import { compact } from '../../util/array.js';
1
2
  import { _load } from '../../util/loader.js';
2
3
  import { timerify } from '../../util/performance.js';
3
4
  import { hasDependency } from '../../util/plugin.js';
@@ -15,6 +16,6 @@ const findVitestDependencies = async (configFilePath) => {
15
16
  const environments = cfg.environment ? [getEnvPackageName(cfg.environment)] : [];
16
17
  const reporters = getExternalReporters(cfg.reporters);
17
18
  const coverage = cfg.coverage?.provider ? [`@vitest/coverage-${cfg.coverage.provider}`] : [];
18
- return [...environments, ...reporters, ...coverage];
19
+ return compact([...environments, ...reporters, ...coverage]);
19
20
  };
20
21
  export const findDependencies = timerify(findVitestDependencies);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://github.com/webpro/knip",
6
6
  "repository": "github:webpro/knip",
@@ -38,27 +38,27 @@
38
38
  "schema.json"
39
39
  ],
40
40
  "dependencies": {
41
- "@esbuild-kit/esm-loader": "2.5.4",
42
- "@npmcli/map-workspaces": "3.0.1",
43
- "@snyk/github-codeowners": "1.1.0",
44
- "chalk": "5.2.0",
45
- "easy-table": "1.2.0",
46
- "esbuild": "0.17.0",
41
+ "@esbuild-kit/esm-loader": "^2.5.4",
42
+ "@npmcli/map-workspaces": "^3.0.1",
43
+ "@snyk/github-codeowners": "^1.1.0",
44
+ "chalk": "^5.2.0",
45
+ "easy-table": "^1.2.0",
46
+ "esbuild": "^0.17.3",
47
47
  "esbuild-register": "3.4.2",
48
- "eslint": "8.32.0",
49
- "fast-glob": "3.2.12",
50
- "get-tsconfig": "4.3.0",
51
- "globby": "13.1.3",
52
- "js-yaml": "4.1.0",
53
- "micromatch": "4.0.5",
54
- "nano-memoize": "2.0.0",
55
- "patch-package": "6.5.1",
56
- "pretty-ms": "8.0.0",
57
- "strip-json-comments": "5.0.0",
58
- "summary": "2.1.0",
59
- "ts-morph": "17.0.1",
60
- "ts-morph-helpers": "0.6.3",
61
- "zod": "3.20.2"
48
+ "eslint": "^8.32.0",
49
+ "fast-glob": "^3.2.12",
50
+ "get-tsconfig": "^4.3.0",
51
+ "globby": "^13.1.3",
52
+ "js-yaml": "^4.1.0",
53
+ "micromatch": "^4.0.5",
54
+ "nano-memoize": "^2.0.0",
55
+ "patch-package": "^6.5.1",
56
+ "pretty-ms": "^8.0.0",
57
+ "strip-json-comments": "^5.0.0",
58
+ "summary": "^2.1.0",
59
+ "ts-morph": "^17.0.1",
60
+ "ts-morph-helpers": "^0.6.3",
61
+ "zod": "^3.20.2"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@jest/types": "29.3.1",
@@ -69,10 +69,10 @@
69
69
  "@types/node": "18.11.18",
70
70
  "@types/npmcli__map-workspaces": "3.0.0",
71
71
  "@types/webpack": "5.28.0",
72
- "@typescript-eslint/eslint-plugin": "5.48.1",
73
- "@typescript-eslint/parser": "5.48.1",
72
+ "@typescript-eslint/eslint-plugin": "5.48.2",
73
+ "@typescript-eslint/parser": "5.48.2",
74
74
  "eslint-import-resolver-typescript": "3.5.3",
75
- "eslint-plugin-import": "2.26.0",
75
+ "eslint-plugin-import": "2.27.5",
76
76
  "globstar": "1.0.0",
77
77
  "release-it": "15.6.0",
78
78
  "remark-cli": "11.0.0",