knip 2.7.0 → 2.8.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
@@ -646,7 +646,27 @@ Many thanks to some of the early adopters of Knip:
646
646
  - [release-it][27]
647
647
  - [Template TypeScript Node Package][28]
648
648
 
649
- ## Knip?!
649
+ ## 🐣 Super Secret Easter Egg Boost 🚀
650
+
651
+ Running Knip on large workspaces with many packages may feel a bit sluggish. Knip looks up `.gitignore` files and uses
652
+ them to filter out matching entry and project files. This increases correctness. However, you might want to disable that
653
+ with `--no-gitignore` and enjoy a significant performance boost. Depending on the contents of the `.gitignore` files,
654
+ the reported issues may be the same. To help determine whether this trade-off might be worth it for you, first check the
655
+ difference in unused files:
656
+
657
+ ```shell
658
+ diff <(knip --no-gitignore --include files) <(knip --include files)
659
+ ```
660
+
661
+ And to measure the difference of this flag in seconds:
662
+
663
+ ```shell
664
+ SECONDS=0; knip > /dev/null; t1=$SECONDS; SECONDS=0; knip --no-gitignore > /dev/null; t2=$SECONDS; echo "Difference: $((t1 - t2)) seconds"
665
+ ```
666
+
667
+ ⏲️ Analysis on a large project went from 33 down to 9 seconds (that's >70% faster). Happy Easter! 🐣
668
+
669
+ ## Knip
650
670
 
651
671
  Knip is Dutch for a "cut". A Dutch expression is "to be ge**knip**t for something", which means to be perfectly suited
652
672
  for the job. I'm motivated to make Knip perfectly suited for the job of cutting projects to perfection! ✂️
@@ -44,6 +44,7 @@ export declare class ConfigurationChief {
44
44
  duplicates: import("./types/issues.js").IssueSeverity;
45
45
  enumMembers: import("./types/issues.js").IssueSeverity;
46
46
  classMembers: import("./types/issues.js").IssueSeverity;
47
+ binaries: import("./types/issues.js").IssueSeverity;
47
48
  };
48
49
  include: string[];
49
50
  exclude: string[];
@@ -67,5 +68,6 @@ export declare class ConfigurationChief {
67
68
  getIssueTypesToReport(): import("./types/issues.js").Report;
68
69
  findWorkspaceByFilePath(filePath: string): Workspace | undefined;
69
70
  findWorkspaceByPackageName(packageName: string): Workspace | undefined;
71
+ getUnusedIgnoredWorkspaces(): string[];
70
72
  }
71
73
  export {};
@@ -264,4 +264,9 @@ export class ConfigurationChief {
264
264
  findWorkspaceByPackageName(packageName) {
265
265
  return this.workspaces.find(workspace => workspace.pkgName === packageName);
266
266
  }
267
+ getUnusedIgnoredWorkspaces() {
268
+ const ignoredWorkspaceNames = this.config.ignoreWorkspaces;
269
+ const workspaceNames = this.getAllWorkspaces();
270
+ return ignoredWorkspaceNames.filter(workspaceName => !workspaceNames.includes(workspaceName));
271
+ }
267
272
  }
@@ -1,5 +1,5 @@
1
1
  import { Workspace } from './ConfigurationChief.js';
2
- import type { Issue } from './types/issues.js';
2
+ import type { ConfigurationHints, Issue } from './types/issues.js';
3
3
  import type { WorkspaceManifests } from './types/workspace.js';
4
4
  import type { PeerDependencies, InstalledBinaries } from './types/workspace.js';
5
5
  import type { PackageJson } from 'type-fest';
@@ -10,6 +10,7 @@ export declare class DependencyDeputy {
10
10
  isStrict: boolean;
11
11
  _manifests: WorkspaceManifests;
12
12
  referencedDependencies: Map<string, Set<string>>;
13
+ referencedBinaries: Map<string, Set<string>>;
13
14
  peerDependencies: Map<string, PeerDependencies>;
14
15
  installedBinaries: Map<string, InstalledBinaries>;
15
16
  constructor({ isStrict }: Options);
@@ -38,13 +39,16 @@ export declare class DependencyDeputy {
38
39
  setInstalledBinaries(workspaceName: string, installedBinaries: Map<string, Set<string>>): void;
39
40
  getInstalledBinaries(workspaceName: string): InstalledBinaries | undefined;
40
41
  addReferencedDependency(workspaceName: string, packageName: string): void;
42
+ addReferencedBinary(workspaceName: string, binaryName: string): void;
41
43
  addPeerDependencies(workspaceName: string, peerDependencies: Map<string, Set<string>>): void;
42
44
  getPeerDependencies(workspaceName: string, dependency: string): string[];
43
45
  maybeAddReferencedExternalDependency(workspace: Workspace, packageName: string): boolean;
46
+ maybeAddReferencedBinary(workspace: Workspace, binaryName: string): boolean;
44
47
  private isInDependencies;
45
48
  settleDependencyIssues(): {
46
49
  dependencyIssues: Issue[];
47
50
  devDependencyIssues: Issue[];
51
+ configurationHints: ConfigurationHints;
48
52
  };
49
53
  }
50
54
  export {};
@@ -1,16 +1,17 @@
1
1
  import { isBuiltin } from 'node:module';
2
- import { fromBinary, isBinary } from './binaries/util.js';
3
2
  import { IGNORE_DEFINITELY_TYPED, IGNORED_DEPENDENCIES, IGNORED_GLOBAL_BINARIES } from './constants.js';
4
3
  import { isDefinitelyTyped, getDefinitelyTypedFor, getPackageFromDefinitelyTyped } from './util/modules.js';
5
4
  export class DependencyDeputy {
6
5
  isStrict;
7
6
  _manifests = new Map();
8
7
  referencedDependencies;
8
+ referencedBinaries;
9
9
  peerDependencies;
10
10
  installedBinaries;
11
11
  constructor({ isStrict }) {
12
12
  this.isStrict = isStrict;
13
13
  this.referencedDependencies = new Map();
14
+ this.referencedBinaries = new Map();
14
15
  this.peerDependencies = new Map();
15
16
  this.installedBinaries = new Map();
16
17
  }
@@ -60,6 +61,12 @@ export class DependencyDeputy {
60
61
  }
61
62
  this.referencedDependencies.get(workspaceName)?.add(packageName);
62
63
  }
64
+ addReferencedBinary(workspaceName, binaryName) {
65
+ if (!this.referencedBinaries.has(workspaceName)) {
66
+ this.referencedBinaries.set(workspaceName, new Set());
67
+ }
68
+ this.referencedBinaries.get(workspaceName)?.add(binaryName);
69
+ }
63
70
  addPeerDependencies(workspaceName, peerDependencies) {
64
71
  this.peerDependencies.set(workspaceName, peerDependencies);
65
72
  }
@@ -73,24 +80,6 @@ export class DependencyDeputy {
73
80
  return true;
74
81
  const workspaceNames = this.isStrict ? [workspace.name] : [workspace.name, ...[...workspace.ancestors].reverse()];
75
82
  const closestWorkspaceName = workspaceNames.find(name => this.isInDependencies(name, packageName));
76
- if (isBinary(packageName)) {
77
- const binaryName = fromBinary(packageName);
78
- if (IGNORED_GLOBAL_BINARIES.includes(binaryName))
79
- return true;
80
- if (this.getWorkspaceManifest(workspace.name)?.ignoreBinaries.includes(binaryName))
81
- return true;
82
- for (const name of workspaceNames) {
83
- const binaries = this.getInstalledBinaries(name);
84
- if (binaries?.has(binaryName)) {
85
- const dependencies = binaries.get(binaryName);
86
- if (dependencies?.size) {
87
- dependencies.forEach(dependency => this.addReferencedDependency(name, dependency));
88
- return true;
89
- }
90
- }
91
- }
92
- return false;
93
- }
94
83
  if (this.getWorkspaceManifest(workspace.name)?.ignoreDependencies.includes(packageName))
95
84
  return true;
96
85
  const typesPackageName = !isDefinitelyTyped(packageName) && getDefinitelyTypedFor(packageName);
@@ -102,6 +91,25 @@ export class DependencyDeputy {
102
91
  }
103
92
  return false;
104
93
  }
94
+ maybeAddReferencedBinary(workspace, binaryName) {
95
+ if (IGNORED_GLOBAL_BINARIES.includes(binaryName))
96
+ return true;
97
+ this.addReferencedBinary(workspace.name, binaryName);
98
+ if (this.getWorkspaceManifest(workspace.name)?.ignoreBinaries.includes(binaryName))
99
+ return true;
100
+ const workspaceNames = this.isStrict ? [workspace.name] : [workspace.name, ...[...workspace.ancestors].reverse()];
101
+ for (const name of workspaceNames) {
102
+ const binaries = this.getInstalledBinaries(name);
103
+ if (binaries?.has(binaryName)) {
104
+ const dependencies = binaries.get(binaryName);
105
+ if (dependencies?.size) {
106
+ dependencies.forEach(dependency => this.addReferencedDependency(name, dependency));
107
+ return true;
108
+ }
109
+ }
110
+ }
111
+ return false;
112
+ }
105
113
  isInDependencies(workspaceName, packageName) {
106
114
  const manifest = this._manifests.get(workspaceName);
107
115
  if (!manifest)
@@ -112,8 +120,10 @@ export class DependencyDeputy {
112
120
  settleDependencyIssues() {
113
121
  const dependencyIssues = [];
114
122
  const devDependencyIssues = [];
123
+ const configurationHints = new Set();
115
124
  for (const [workspaceName, { manifestPath, ignoreDependencies, ignoreBinaries }] of this._manifests.entries()) {
116
125
  const referencedDependencies = this.referencedDependencies.get(workspaceName);
126
+ const referencedBinaries = this.referencedBinaries.get(workspaceName);
117
127
  const installedBinaries = this.getInstalledBinaries(workspaceName);
118
128
  const ignoreBins = [...IGNORED_GLOBAL_BINARIES, ...ignoreBinaries];
119
129
  const ignoreDeps = [...IGNORED_DEPENDENCIES, ...ignoreDependencies];
@@ -146,17 +156,25 @@ export class DependencyDeputy {
146
156
  }
147
157
  return false;
148
158
  };
149
- this.getProductionDependencies(workspaceName)
150
- .filter(isNotIgnoredDependency)
159
+ const pd = this.getProductionDependencies(workspaceName);
160
+ const dd = this.getDevDependencies(workspaceName);
161
+ pd.filter(isNotIgnoredDependency)
151
162
  .filter(isNotIgnoredBinary)
152
163
  .filter(isNotReferencedDependency)
153
164
  .forEach(symbol => dependencyIssues.push({ type: 'dependencies', filePath: manifestPath, symbol }));
154
- this.getDevDependencies(workspaceName)
155
- .filter(isNotIgnoredDependency)
165
+ dd.filter(isNotIgnoredDependency)
156
166
  .filter(isNotIgnoredBinary)
157
167
  .filter(isNotReferencedDependency)
158
168
  .forEach(symbol => devDependencyIssues.push({ type: 'devDependencies', filePath: manifestPath, symbol }));
169
+ const isReferencedDep = (name) => ![...pd, ...dd].includes(name) && referencedDependencies?.has(name);
170
+ const isReferencedBin = (name) => !installedBinaries?.has(name) && referencedBinaries?.has(name);
171
+ ignoreDependencies
172
+ .filter(packageName => IGNORED_DEPENDENCIES.includes(packageName) || !isReferencedDep(packageName))
173
+ .forEach(identifier => configurationHints.add({ workspaceName, identifier, type: 'ignoreDependencies' }));
174
+ ignoreBinaries
175
+ .filter(binaryName => IGNORED_GLOBAL_BINARIES.includes(binaryName) || !isReferencedBin(binaryName))
176
+ .forEach(identifier => configurationHints.add({ workspaceName, identifier, type: 'ignoreBinaries' }));
159
177
  }
160
- return { dependencyIssues, devDependencyIssues };
178
+ return { dependencyIssues, devDependencyIssues, configurationHints };
161
179
  }
162
180
  }
@@ -1,4 +1,4 @@
1
- import type { Issue, Rules } from './types/issues.js';
1
+ import type { ConfigurationHint, Issue, Rules } from './types/issues.js';
2
2
  type IssueCollectorOptions = {
3
3
  cwd: string;
4
4
  rules: Rules;
@@ -9,6 +9,7 @@ export declare class IssueCollector {
9
9
  private issues;
10
10
  private counters;
11
11
  private referencedFiles;
12
+ private configurationHints;
12
13
  constructor({ cwd, rules }: IssueCollectorOptions);
13
14
  addFileCounts({ processed, unused }: {
14
15
  processed: number;
@@ -16,9 +17,11 @@ export declare class IssueCollector {
16
17
  }): void;
17
18
  addFilesIssues(filePaths: string[]): void;
18
19
  addIssue(issue: Issue): void;
20
+ addConfigurationHint(issue: ConfigurationHint): void;
19
21
  getIssues(): {
20
22
  issues: import("./types/issues.js").Issues;
21
23
  counters: import("./types/issues.js").Counters;
24
+ configurationHints: Set<unknown>;
22
25
  };
23
26
  }
24
27
  export {};
@@ -6,6 +6,7 @@ export class IssueCollector {
6
6
  issues = initIssues();
7
7
  counters = initCounters();
8
8
  referencedFiles = new Set();
9
+ configurationHints = new Set();
9
10
  constructor({ cwd, rules }) {
10
11
  this.cwd = cwd;
11
12
  this.rules = rules;
@@ -32,10 +33,14 @@ export class IssueCollector {
32
33
  this.counters[issue.type]++;
33
34
  }
34
35
  }
36
+ addConfigurationHint(issue) {
37
+ this.configurationHints.add(issue);
38
+ }
35
39
  getIssues() {
36
40
  return {
37
41
  issues: this.issues,
38
42
  counters: this.counters,
43
+ configurationHints: this.configurationHints,
39
44
  };
40
45
  }
41
46
  }
package/dist/cli.js CHANGED
@@ -23,7 +23,7 @@ const printReport = reporter in reporters ? reporters[reporter] : await _load(re
23
23
  const run = async () => {
24
24
  try {
25
25
  const perfObserver = new Performance(isObservePerf);
26
- const { report, issues, counters, rules } = await main({
26
+ const { report, issues, counters, rules, configurationHints } = await main({
27
27
  cwd,
28
28
  tsConfigFile: tsConfig,
29
29
  gitignore: !isNoGitIgnore,
@@ -31,7 +31,7 @@ const run = async () => {
31
31
  isProduction,
32
32
  isShowProgress,
33
33
  });
34
- await printReport({ report, issues, cwd, isProduction, options: reporterOptions });
34
+ await printReport({ report, issues, configurationHints, cwd, isProduction, options: reporterOptions });
35
35
  const totalErrorCount = Object.keys(report)
36
36
  .filter(reportGroup => report[reportGroup] && rules[reportGroup] === 'error')
37
37
  .reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
package/dist/constants.js CHANGED
@@ -6,6 +6,7 @@ export const TEST_FILE_PATTERNS = [
6
6
  '**/{test,__tests__}/**/*.{js,jsx,ts,tsx,mjs,cjs}',
7
7
  ];
8
8
  export const IGNORED_GLOBAL_BINARIES = [
9
+ 'bash',
9
10
  'bun',
10
11
  'deno',
11
12
  'git',
@@ -14,6 +15,7 @@ export const IGNORED_GLOBAL_BINARIES = [
14
15
  'npx',
15
16
  'pnpm',
16
17
  'yarn',
18
+ 'cat',
17
19
  'cd',
18
20
  'cp',
19
21
  'echo',
@@ -49,6 +51,7 @@ export const ISSUE_TYPES = [
49
51
  'dependencies',
50
52
  'devDependencies',
51
53
  'unlisted',
54
+ 'binaries',
52
55
  'unresolved',
53
56
  'exports',
54
57
  'nsExports',
@@ -63,6 +66,7 @@ export const ISSUE_TYPE_TITLE = {
63
66
  dependencies: 'Unused dependencies',
64
67
  devDependencies: 'Unused devDependencies',
65
68
  unlisted: 'Unlisted dependencies',
69
+ binaries: 'Unresolved binaries',
66
70
  unresolved: 'Unresolved imports',
67
71
  exports: 'Unused exports',
68
72
  nsExports: 'Unused exports in namespaces',
package/dist/index.d.ts CHANGED
@@ -6,4 +6,5 @@ export declare const main: (unresolvedConfiguration: CommandLineOptions) => Prom
6
6
  issues: import("./types/issues.js").Issues;
7
7
  counters: import("./types/issues.js").Counters;
8
8
  rules: import("./types/issues.js").Rules;
9
+ configurationHints: Set<unknown>;
9
10
  }>;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { _getDependenciesFromScripts } from './binaries/index.js';
2
+ import { fromBinary, isBinary } from './binaries/util.js';
2
3
  import { ConfigurationChief } from './ConfigurationChief.js';
3
4
  import { ConsoleStreamer } from './ConsoleStreamer.js';
4
5
  import { ROOT_WORKSPACE_NAME } from './constants.js';
@@ -47,14 +48,16 @@ export const main = async (unresolvedConfiguration) => {
47
48
  }
48
49
  }
49
50
  else {
50
- if (isInNodeModules(specifier)) {
51
- const packageName = getPackageNameFromFilePath(specifier);
52
- const isHandled = deputy.maybeAddReferencedExternalDependency(workspace, packageName);
51
+ if (isBinary(specifier)) {
52
+ const binaryName = fromBinary(specifier);
53
+ const isHandled = deputy.maybeAddReferencedBinary(workspace, binaryName);
53
54
  if (!isHandled)
54
- collector.addIssue({ type: 'unlisted', filePath: containingFilePath, symbol: packageName });
55
+ collector.addIssue({ type: 'binaries', filePath: containingFilePath, symbol: binaryName });
55
56
  }
56
57
  else {
57
- const packageName = getPackageNameFromModuleSpecifier(specifier);
58
+ const packageName = isInNodeModules(specifier)
59
+ ? getPackageNameFromFilePath(specifier)
60
+ : getPackageNameFromModuleSpecifier(specifier);
58
61
  const isHandled = deputy.maybeAddReferencedExternalDependency(workspace, packageName);
59
62
  if (!isHandled)
60
63
  collector.addIssue({ type: 'unlisted', filePath: containingFilePath, symbol: specifier });
@@ -296,12 +299,15 @@ export const main = async (unresolvedConfiguration) => {
296
299
  }
297
300
  }
298
301
  if (isReportDependencies) {
299
- const { dependencyIssues, devDependencyIssues } = deputy.settleDependencyIssues();
302
+ const { dependencyIssues, devDependencyIssues, configurationHints } = deputy.settleDependencyIssues();
300
303
  dependencyIssues.forEach(issue => collector.addIssue(issue));
301
304
  if (!isProduction)
302
305
  devDependencyIssues.forEach(issue => collector.addIssue(issue));
306
+ configurationHints.forEach(hint => collector.addConfigurationHint(hint));
303
307
  }
304
- const { issues, counters } = collector.getIssues();
308
+ const unusedIgnoredWorkspaces = chief.getUnusedIgnoredWorkspaces();
309
+ unusedIgnoredWorkspaces.forEach(identifier => collector.addConfigurationHint({ type: 'ignoreWorkspaces', identifier }));
310
+ const { issues, counters, configurationHints } = collector.getIssues();
305
311
  streamer.clear();
306
- return { report, issues, counters, rules };
312
+ return { report, issues, counters, rules, configurationHints };
307
313
  };
@@ -1,5 +1,5 @@
1
1
  declare const _default: {
2
- symbols: ({ report, issues }: import("../index.js").ReporterOptions) => void;
2
+ symbols: ({ report, issues, configurationHints }: import("../index.js").ReporterOptions) => void;
3
3
  compact: ({ report, issues }: import("../index.js").ReporterOptions) => void;
4
4
  codeowners: ({ report, issues, options }: import("../index.js").ReporterOptions) => void;
5
5
  json: ({ report, issues, options }: import("../index.js").ReporterOptions) => Promise<void>;
@@ -1,3 +1,3 @@
1
1
  import type { ReporterOptions } from '../types/issues.js';
2
- declare const _default: ({ report, issues }: ReporterOptions) => void;
2
+ declare const _default: ({ report, issues, configurationHints }: ReporterOptions) => void;
3
3
  export default _default;
@@ -1,5 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import EasyTable from 'easy-table';
3
+ import { ROOT_WORKSPACE_NAME } from '../constants.js';
3
4
  import { relative } from '../util/path.js';
4
5
  import { getTitle, logTitle, logIssueSet, identity } from './util.js';
5
6
  const TRUNCATE_WIDTH = 40;
@@ -16,7 +17,7 @@ const logIssueRecord = (issues) => {
16
17
  });
17
18
  console.log(table.sort(['filePath', 'parentSymbol', 'symbol']).print().trim());
18
19
  };
19
- export default ({ report, issues }) => {
20
+ export default ({ report, issues, configurationHints }) => {
20
21
  const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
21
22
  let totalIssues = 0;
22
23
  for (const [reportType, isReportType] of Object.entries(report)) {
@@ -38,6 +39,15 @@ export default ({ report, issues }) => {
38
39
  totalIssues = totalIssues + issuesForType.length;
39
40
  }
40
41
  }
42
+ if (configurationHints.size > 0) {
43
+ logTitle('Configuration issues', configurationHints.size);
44
+ configurationHints.forEach(hint => {
45
+ const { type, workspaceName, identifier } = hint;
46
+ const message = `Unused item in ${type}`;
47
+ const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : ``;
48
+ console.warn(chalk.grey(`${message}${workspace}:`), identifier);
49
+ });
50
+ }
41
51
  if (totalIssues === 0) {
42
52
  console.log('✂️ Excellent, Knip found no issues.');
43
53
  }
@@ -23,6 +23,7 @@ export type Issues = {
23
23
  dependencies: IssueRecords;
24
24
  devDependencies: IssueRecords;
25
25
  unlisted: IssueRecords;
26
+ binaries: IssueRecords;
26
27
  unresolved: IssueRecords;
27
28
  exports: IssueRecords;
28
29
  types: IssueRecords;
@@ -41,6 +42,7 @@ export type Counters = Record<IssueType | 'processed' | 'total', number>;
41
42
  export type ReporterOptions = {
42
43
  report: Report;
43
44
  issues: Issues;
45
+ configurationHints: ConfigurationHints;
44
46
  cwd: string;
45
47
  workingDir: string;
46
48
  isProduction: boolean;
@@ -49,3 +51,9 @@ export type ReporterOptions = {
49
51
  export type Reporter = (options: ReporterOptions) => void;
50
52
  export type IssueSeverity = 'error' | 'warn' | 'off';
51
53
  export type Rules = Record<IssueType, IssueSeverity>;
54
+ export type ConfigurationHints = Set<ConfigurationHint>;
55
+ export type ConfigurationHint = {
56
+ type: 'ignoreBinaries' | 'ignoreDependencies' | 'ignoreWorkspaces';
57
+ identifier: string;
58
+ workspaceName?: string;
59
+ };
@@ -1,5 +1,6 @@
1
1
  import { isBuiltin } from 'node:module';
2
2
  import ts from 'typescript';
3
+ import { getOrSet } from '../util/map.js';
3
4
  import { isInNodeModules } from '../util/path.js';
4
5
  import { isDeclarationFileExtension, isAccessExpression, getAccessExpressionName } from './ast-helpers.js';
5
6
  import getExportVisitors from './visitors/exports/index.js';
@@ -22,17 +23,14 @@ export const getImportsAndExports = (sourceFile, options) => {
22
23
  const addInternalImport = ({ identifier, specifier, symbol, filePath, isDynamic }) => {
23
24
  const isStar = identifier === '*';
24
25
  const isReExported = Boolean(isStar && !symbol);
25
- if (!internalImports.has(filePath)) {
26
- internalImports.set(filePath, {
27
- specifier,
28
- isStar,
29
- isReExported,
30
- isReExportedBy: new Set(),
31
- symbols: new Set(),
32
- isDynamic,
33
- });
34
- }
35
- const internalImport = internalImports.get(filePath);
26
+ const internalImport = getOrSet(internalImports, filePath, {
27
+ specifier,
28
+ isStar,
29
+ isReExported,
30
+ isReExportedBy: new Set(),
31
+ symbols: new Set(),
32
+ isDynamic,
33
+ });
36
34
  if (isReExported) {
37
35
  internalImport.isReExported = isReExported;
38
36
  internalImport.isReExportedBy.add(sourceFile.fileName);
@@ -1,7 +1,7 @@
1
1
  import { ISSUE_TYPES } from '../constants.js';
2
2
  export const getIncludedIssueTypes = (cliArgs, { include = [], exclude = [], isProduction = false } = {}) => {
3
3
  if (cliArgs.dependencies) {
4
- cliArgs.include = [...cliArgs.include, 'dependencies', 'unlisted', 'unresolved'];
4
+ cliArgs.include = [...cliArgs.include, 'dependencies', 'unlisted', 'binaries', 'unresolved'];
5
5
  }
6
6
  if (cliArgs.exports) {
7
7
  const exports = ['exports', 'nsExports', 'classMembers', 'types', 'nsTypes', 'enumMembers', 'duplicates'];
package/dist/util/glob.js CHANGED
@@ -16,9 +16,13 @@ export const hasNoProductionSuffix = (pattern) => !pattern.endsWith('!');
16
16
  const removeProductionSuffix = (pattern) => pattern.replace(/!$/, '');
17
17
  const negatedLast = (pattern) => (pattern.startsWith('!') ? 1 : -1);
18
18
  const glob = async ({ cwd, workingDir = cwd, patterns, ignore = [], gitignore = true }) => {
19
+ if (patterns.length === 0)
20
+ return [];
19
21
  const relativePath = relative(cwd, workingDir);
20
22
  const prepend = (pattern) => prependDirToPattern(relativePath, pattern);
21
23
  const globPatterns = compact([patterns].flat().map(prepend).map(removeProductionSuffix)).sort(negatedLast);
24
+ if (globPatterns[0].startsWith('!'))
25
+ return [];
22
26
  const ignorePatterns = compact([...ignore, '**/node_modules/**']);
23
27
  debugLogObject(`Globbing (${relativePath || ROOT_WORKSPACE_NAME})`, { cwd, globPatterns, ignorePatterns });
24
28
  return globby(globPatterns, {
@@ -29,12 +33,16 @@ const glob = async ({ cwd, workingDir = cwd, patterns, ignore = [], gitignore =
29
33
  dot: true,
30
34
  });
31
35
  };
32
- const pureGlob = async ({ cwd, patterns, ignore = [], gitignore = true }) => globby(patterns, {
33
- cwd,
34
- ignore: [...ignore, '**/node_modules/**'],
35
- gitignore,
36
- absolute: true,
37
- });
36
+ const pureGlob = async ({ cwd, patterns, ignore = [], gitignore = true }) => {
37
+ if (patterns.length === 0)
38
+ return [];
39
+ return globby(patterns, {
40
+ cwd,
41
+ ignore: [...ignore, '**/node_modules/**'],
42
+ gitignore,
43
+ absolute: true,
44
+ });
45
+ };
38
46
  const firstGlob = async ({ cwd, patterns }) => {
39
47
  const stream = fg.stream(patterns.map(removeProductionSuffix), { cwd, ignore: ['**/node_modules/**'] });
40
48
  for await (const entry of stream) {
@@ -0,0 +1 @@
1
+ export declare const getOrSet: <K extends string, V, T extends Map<K, V>>(map: T, key: K, value: V) => NonNullable<V>;
@@ -0,0 +1,6 @@
1
+ export const getOrSet = (map, key, value) => {
2
+ if (!map.has(key)) {
3
+ map.set(key, value);
4
+ }
5
+ return map.get(key);
6
+ };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.7.0";
1
+ export declare const version = "2.8.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.7.0';
1
+ export const version = '2.8.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.7.0",
3
+ "version": "2.8.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",
@@ -74,7 +74,7 @@
74
74
  "@typescript-eslint/parser": "5.57.1",
75
75
  "c8": "7.13.0",
76
76
  "eslint": "8.37.0",
77
- "eslint-import-resolver-typescript": "3.5.4",
77
+ "eslint-import-resolver-typescript": "3.5.5",
78
78
  "eslint-plugin-import": "2.27.5",
79
79
  "eslint-plugin-n": "15.7.0",
80
80
  "globstar": "1.0.0",
@@ -83,7 +83,7 @@
83
83
  "remark-cli": "11.0.0",
84
84
  "remark-preset-webpro": "0.0.2",
85
85
  "tsx": "3.12.6",
86
- "type-fest": "3.7.2"
86
+ "type-fest": "3.8.0"
87
87
  },
88
88
  "engines": {
89
89
  "node": ">=16.17.0 <17 || >=18.6.0"