knip 1.0.2 → 1.1.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
@@ -97,6 +97,8 @@ Use `npm run knip` to analyze the project and output unused files, dependencies
97
97
  --no-gitignore Don't use .gitignore
98
98
  --include Report only provided issue type(s), can be comma-separated or repeated (1)
99
99
  --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)
100
+ --dependencies Shortcut for --include dependencies,unlisted
101
+ --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates
100
102
  --no-progress Don't show dynamic progress updates
101
103
  --reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
102
104
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
@@ -159,6 +161,8 @@ Use `--exclude` to ignore reports you're not interested in:
159
161
 
160
162
  knip --include files --exclude classMembers,enumMembers
161
163
 
164
+ Use `--dependencies` or `--exports` as shortcuts to combine groups of related types.
165
+
162
166
  Still not happy with the results? Getting too much output/false positives? The [FAQ][9] may be useful. Feel free to open
163
167
  an issue and I'm happy to look into it.
164
168
 
@@ -404,7 +408,7 @@ how many dependencies are used and how much the project diverges from the defaul
404
408
  One important goal of Knip is to minimize the amount of configuration necessary. So when there are feasible ways to
405
409
  infer things automatically, reducing the amount of configuration, please open an issue.
406
410
 
407
- ### How do I handle too much output/false positives?
411
+ ### How do I handle too many output/false positives?
408
412
 
409
413
  #### Too many unused files
410
414
 
@@ -430,6 +434,17 @@ When the project is a library and the exports are meant to be used by consumers
430
434
  1. By default, unused exports of `entry` files are not reported, so you can add the containing file to it.
431
435
  2. The exported values or types can be marked [using the JSDoc `@public` tag][42].
432
436
 
437
+ ### How to start using Knip in CI while having too many issues to sort out?
438
+
439
+ Eventually this type of QA only really works when it's tied to an automated workflow. But with too many issues to
440
+ resolve this might not be feasible right away, especially in existing larger codebase. Here are a few options:
441
+
442
+ - Use `--no-exit-code` for exit code 0 in CI.
443
+ - Use `--include` (or `--exclude`) to report only the issue types don't have errors.
444
+ - Use `ignore` (for files and directories) and `ignoreDependencies` to filter out some problematic areas.
445
+
446
+ All of this is hiding problems, so please make sure to plan for fixing them and/or open issues here for false positives.
447
+
433
448
  ## Comparison
434
449
 
435
450
  This table is an ongoing comparison. Based on their docs (please report any mistakes):
@@ -460,14 +475,14 @@ This table is an ongoing comparison. Based on their docs (please report any mist
460
475
  The following commands are similar:
461
476
 
462
477
  depcheck
463
- knip --include dependencies,unlisted
478
+ knip --dependencies
464
479
 
465
480
  ### unimported
466
481
 
467
482
  The following commands are similar:
468
483
 
469
484
  unimported
470
- knip --production --include files,dependencies,unlisted
485
+ knip --production --dependencies --include files
471
486
 
472
487
  Also see [production mode][48].
473
488
 
@@ -477,6 +492,7 @@ The following commands are similar:
477
492
 
478
493
  ts-unused-exports
479
494
  knip --include exports,types,nsExports,nsTypes
495
+ knip --exports # Adds unused enum and class members
480
496
 
481
497
  ### ts-prune
482
498
 
@@ -484,6 +500,7 @@ The following commands are similar:
484
500
 
485
501
  ts-prune
486
502
  knip --include exports,types
503
+ knip --exports # Adds unused exports/types in namespaces and unused enum/class members
487
504
 
488
505
  ## TypeScript language services
489
506
 
@@ -11,7 +11,7 @@ import { findFile, loadJSON } from './util/fs.js';
11
11
  import { ensurePosixPath } from './util/glob.js';
12
12
  import { resolveIncludedIssueTypes } from './util/resolve-included-issue-types.js';
13
13
  import { byPathDepth } from './util/workspace.js';
14
- const { values: { config: rawConfigArg, workspace: rawWorkspaceArg, include = [], exclude = [] }, } = parsedArgs;
14
+ const { values: { config: rawConfigArg, workspace: rawWorkspaceArg, include = [], exclude = [], dependencies = false, exports = false, }, } = parsedArgs;
15
15
  const defaultWorkspaceConfig = {
16
16
  entry: ['index.{js,ts,tsx}!', 'src/index.{js,ts,tsx}!'],
17
17
  project: ['**/*.{js,ts,tsx}!'],
@@ -209,7 +209,12 @@ export default class ConfigurationChief {
209
209
  return { entry: [], project: [], ignore: [] };
210
210
  }
211
211
  resolveIncludedIssueTypes() {
212
- return resolveIncludedIssueTypes(include, exclude, {
212
+ return resolveIncludedIssueTypes({
213
+ include,
214
+ exclude,
215
+ dependencies,
216
+ exports,
217
+ }, {
213
218
  include: this.config.include ?? [],
214
219
  exclude: this.config.exclude ?? [],
215
220
  isProduction: this.isProduction,
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ export const main = async (unresolvedConfiguration) => {
29
29
  .map(workspace => workspace.dir)
30
30
  .sort(byPathDepth)
31
31
  .reverse();
32
- const lab = new SourceLab({ report, workspaceDirs });
32
+ const lab = new SourceLab({ report, workspaceDirs, isIncludeEntryExports });
33
33
  const principal = new ProjectPrincipal();
34
34
  for (const { name, dir, config, ancestors } of workspaces) {
35
35
  const isRoot = name === ROOT_WORKSPACE_NAME;
@@ -3,15 +3,17 @@ import type { Report, Issue } from './types/issues.js';
3
3
  type FileLabOptions = {
4
4
  report: Report;
5
5
  workspaceDirs: string[];
6
+ isIncludeEntryExports: boolean;
6
7
  };
7
8
  export default class SourceLab {
8
9
  report: Report;
9
10
  workspaceDirs: string[];
11
+ isIncludeEntryExports: boolean;
10
12
  skipExportsAnalysis: Set<unknown>;
11
13
  isReportExports: boolean;
12
14
  isReportValues: boolean;
13
15
  isReportTypes: boolean;
14
- constructor({ report, workspaceDirs }: FileLabOptions);
16
+ constructor({ report, workspaceDirs, isIncludeEntryExports }: FileLabOptions);
15
17
  skipExportsAnalysisFor(filePath: string | string[]): void;
16
18
  analyzeSourceFile(sourceFile: SourceFile): Set<Issue>;
17
19
  private analyzeExports;
@@ -5,13 +5,15 @@ import { getType } from './util/type.js';
5
5
  export default class SourceLab {
6
6
  report;
7
7
  workspaceDirs;
8
+ isIncludeEntryExports;
8
9
  skipExportsAnalysis;
9
10
  isReportExports;
10
11
  isReportValues;
11
12
  isReportTypes;
12
- constructor({ report, workspaceDirs }) {
13
+ constructor({ report, workspaceDirs, isIncludeEntryExports }) {
13
14
  this.report = report;
14
15
  this.workspaceDirs = workspaceDirs;
16
+ this.isIncludeEntryExports = isIncludeEntryExports;
15
17
  this.skipExportsAnalysis = new Set();
16
18
  this.isReportValues = report.exports || report.nsExports || report.classMembers;
17
19
  this.isReportTypes = report.types || report.nsTypes || report.enumMembers;
@@ -104,7 +106,13 @@ export default class SourceLab {
104
106
  else {
105
107
  if (hasExternalReferences(refs, filePath))
106
108
  return;
107
- if (_findReferencingNamespaceNodes(sourceFile).length > 0) {
109
+ const referencingNodes = _findReferencingNamespaceNodes(sourceFile);
110
+ if (referencingNodes.length > 0) {
111
+ if (!this.isIncludeEntryExports && referencingNodes.length === 1) {
112
+ const referencingSourceFile = referencingNodes.at(0)?.getSourceFile().getFilePath();
113
+ if (this.skipExportsAnalysis.has(referencingSourceFile))
114
+ return;
115
+ }
108
116
  if (type) {
109
117
  issues.add({ type: 'nsTypes', filePath, symbol: identifierText, symbolType: type });
110
118
  }
@@ -1,10 +1,12 @@
1
- export declare const helpText = "knip [options]\n\nOptions:\n -c/--config [file] Configuration file path (default: knip.json, knip.jsonc or package.json#knip)\n -t/--tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --workspace Analyze a single workspace (default: analyze all configured workspaces)\n --include-entry-exports Include unused exports in entry files (without `@public`)\n --ignore Ignore files matching this glob pattern, can be repeated\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --no-progress Don't show dynamic progress updates\n --reporter Select reporter: symbols, compact, codeowners, json (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure running time of expensive functions and display stats table\n\n(1) Issue types: files, dependencies, unlisted, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore info: https://github.com/webpro/knip";
1
+ export declare const helpText = "knip [options]\n\nOptions:\n -c/--config [file] Configuration file path (default: knip.json, knip.jsonc or package.json#knip)\n -t/--tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --workspace Analyze a single workspace (default: analyze all configured workspaces)\n --include-entry-exports Include unused exports in entry files (without `@public`)\n --ignore Ignore files matching this glob pattern, can be repeated\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --dependencies Shortcut for --include dependencies,unlisted\n --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates\n --no-progress Don't show dynamic progress updates\n --reporter Select reporter: symbols, compact, codeowners, json (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure running time of expensive functions and display stats table\n\n(1) Issue types: files, dependencies, unlisted, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore info: https://github.com/webpro/knip";
2
2
  declare const _default: {
3
3
  values: {
4
4
  config: string | undefined;
5
5
  debug: boolean | undefined;
6
6
  'debug-file-filter': string | undefined;
7
+ dependencies: boolean | undefined;
7
8
  exclude: string[] | undefined;
9
+ exports: boolean | undefined;
8
10
  help: boolean | undefined;
9
11
  ignore: string[] | undefined;
10
12
  'include-entry-exports': boolean | undefined;
@@ -12,6 +12,8 @@ Options:
12
12
  --no-gitignore Don't use .gitignore
13
13
  --include Report only provided issue type(s), can be comma-separated or repeated (1)
14
14
  --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)
15
+ --dependencies Shortcut for --include dependencies,unlisted
16
+ --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates
15
17
  --no-progress Don't show dynamic progress updates
16
18
  --reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
17
19
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
@@ -38,7 +40,9 @@ export default parseArgs({
38
40
  config: { type: 'string', short: 'c' },
39
41
  debug: { type: 'boolean' },
40
42
  'debug-file-filter': { type: 'string' },
43
+ dependencies: { type: 'boolean' },
41
44
  exclude: { type: 'string', multiple: true },
45
+ exports: { type: 'boolean' },
42
46
  help: { type: 'boolean', short: 'h' },
43
47
  ignore: { type: 'string', multiple: true },
44
48
  'include-entry-exports': { type: 'boolean' },
@@ -1,8 +1,16 @@
1
1
  import type { Report } from '../types/issues.js';
2
+ type CLIArguments = {
3
+ include: string[];
4
+ exclude: string[];
5
+ dependencies: boolean;
6
+ exports: boolean;
7
+ };
2
8
  type Options = {
3
9
  isProduction?: boolean;
4
10
  include?: string[];
5
11
  exclude?: string[];
12
+ dependencies?: boolean;
13
+ exports?: boolean;
6
14
  };
7
- export declare const resolveIncludedIssueTypes: (includeArg: string[], excludeArg: string[], { include, exclude, isProduction }?: Options) => Report;
15
+ export declare const resolveIncludedIssueTypes: (cliArgs: CLIArguments, { include, exclude, isProduction }?: Options) => Report;
8
16
  export {};
@@ -1,7 +1,14 @@
1
1
  import { ISSUE_TYPES } from '../constants.js';
2
- export const resolveIncludedIssueTypes = (includeArg, excludeArg, { include = [], exclude = [], isProduction = false } = {}) => {
3
- const normalizedIncludesArg = includeArg.map(value => value.split(',')).flat();
4
- const normalizedExcludesArg = excludeArg.map(value => value.split(',')).flat();
2
+ export const resolveIncludedIssueTypes = (cliArgs, { include = [], exclude = [], isProduction = false } = {}) => {
3
+ if (cliArgs.dependencies) {
4
+ cliArgs.include = [...cliArgs.include, 'dependencies', 'unlisted'];
5
+ }
6
+ if (cliArgs.exports) {
7
+ const exports = ['exports', 'nsExports', 'classMembers', 'types', 'nsTypes', 'enumMembers', 'duplicates'];
8
+ cliArgs.include = [...cliArgs.include, ...exports];
9
+ }
10
+ const normalizedIncludesArg = cliArgs.include.map(value => value.split(',')).flat();
11
+ const normalizedExcludesArg = cliArgs.exclude.map(value => value.split(',')).flat();
5
12
  const excludes = exclude.filter(exclude => !normalizedIncludesArg.includes(exclude));
6
13
  const includes = include.filter(include => !normalizedExcludesArg.includes(include));
7
14
  const _include = [normalizedIncludesArg, includes].flat();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "1.0.2",
3
+ "version": "1.1.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",