knip 2.2.4 → 2.3.1

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
@@ -32,16 +32,11 @@ Knip shines in both small and large projects. It's a fresh take on keeping your
32
32
  [![An orange cow with scissors, Van Gogh style][7]][6] <sup>_“An orange cow with scissors, Van Gogh style” - generated
33
33
  with OpenAI_</sup>
34
34
 
35
- ## Migrating to v2.0.0
36
-
37
- Migrating from v1 to v2 requires no changes in configuration. Mostly it's just a whole lot faster! The [release notes
38
- for v2][8] provide more details.
39
-
40
35
  ## Installation
41
36
 
42
37
  npm install -D knip
43
38
 
44
- Knip supports LTS versions of Node.js, and currently requires at least Node.js v16.17 or v18.6. Knip is _cutting edge!_
39
+ Knip supports LTS versions of Node.js, and currently requires at least Node.js v16.17 or v18.6.
45
40
 
46
41
  ## Configuration
47
42
 
@@ -54,9 +49,19 @@ Knip has good defaults and you can run it without any configuration. Here's the
54
49
  }
55
50
  ```
56
51
 
57
- _Actually_, here's the full list of default extensions: `js`, `mjs`, `cjs`, `jsx`, `ts`, `mts`, `cts` and `tsx`.
52
+ Well, almost, this is the full list of default extensions: `js`, `mjs`, `cjs`, `jsx`, `ts`, `mts`, `cts` and `tsx`.
53
+
54
+ ### Entry Files
55
+
56
+ Knip looks for entry files at the default locations above, but also in other places:
57
+
58
+ - The `main`, `bin` and `exports` fields of `package.json`.
59
+ - [Plugins][2] such as for Next.js, Remix, Gatsby or Svelte define entry files so you don't have to.
60
+ - The `scripts` in package.json may also provide entry files that Knip can use.
61
+ - Knip does this for each [workspace][1] it finds.
58
62
 
59
- If this matches your project, you don't need any configuration. Not even a `knip.json` file.
63
+ In other words, Knip looks in many places and you may not need much configuration. In a perfectly boring world where
64
+ everything is according to defaults you don't even need a `knip.json` file.
60
65
 
61
66
  Larger projects tend to have more things customized, and therefore probably get more out of Knip with a configuration
62
67
  file. Let's say you are using `.ts` files excusively and have all source files only in the `src` directory:
@@ -168,19 +173,23 @@ The report contains the following types of issues:
168
173
 
169
174
  - **Unused files**: did not find references to this file
170
175
  - **Unused dependencies**: did not find references to this dependency
171
- - **Unlisted dependencies**: used dependencies, but not listed in package.json
176
+ - **Unused devDependencies**: did not find references to this dependency
177
+ - **Unlisted dependencies**: used dependencies, but not listed in package.json _(1)_
172
178
  - **Unresolved imports**: import specifiers that could not be resolved
173
179
  - **Unused exports**: did not find references to this exported variable
174
- - **Unused exports in namespaces**: did not find direct references to this exported variable _(1)_
180
+ - **Unused exports in namespaces**: did not find direct references to this exported variable _(2)_
175
181
  - **Unused exported types**: did not find references to this exported type
176
- - **Unused exported types in namespaces**: did not find direct references to this exported variable _(1)_
182
+ - **Unused exported types in namespaces**: did not find direct references to this exported variable _(2)_
177
183
  - **Unused exported enum members**: did not find references to this member of the exported enum
178
184
  - **Unused exported class members**: did not find references to this member of the exported class
179
185
  - **Duplicate exports**: the same thing is exported more than once
180
186
 
181
187
  When an issue type has zero issues, it is not shown.
182
188
 
183
- _(1)_ The variable or type is not referenced directly, and has become a member of a namespace. Knip can't find a
189
+ _(1)_ If an unlisted dependency is prefixed with `bin:` it means a binary is missing. This often equals the package
190
+ name, but not always (e.g. `tsc` of `typescript` or `webpack` from `webpack-cli`).
191
+
192
+ _(2)_ The variable or type is not referenced directly, and has become a member of a namespace. Knip can't find a
184
193
  reference to it, so you can _probably_ remove it.
185
194
 
186
195
  ### Output filters
@@ -199,8 +208,8 @@ Use `--exclude` to ignore reports you're not interested in:
199
208
 
200
209
  Use `--dependencies` or `--exports` as shortcuts to combine groups of related types.
201
210
 
202
- Still not happy with the results? Getting too much output/false positives? The [FAQ][9] may be useful. Feel free to open
203
- an issue and I'm happy to look into it. Also see the next section on how to [ignore][10] certain false positives:
211
+ Still not happy with the results? Getting too much output/false positives? The [FAQ][8] may be useful. Feel free to open
212
+ an issue and I'm happy to look into it. Also see the next section on how to [ignore][9] certain false positives:
204
213
 
205
214
  ## Ignore
206
215
 
@@ -381,7 +390,7 @@ to override any defaults. Let's take Cypress for example. By default it uses `cy
381
390
  ### Multi-project repositories
382
391
 
383
392
  Some repositories have a single `package.json`, but consist of multiple projects with potentially lots of configuration
384
- files (such as the [Nx "intregrated repo" style][11]). Let's assume some of these projects are apps and have their own
393
+ files (such as the [Nx "intregrated repo" style][10]). Let's assume some of these projects are apps and have their own
385
394
  Cypress configuration and test files. In that case, we could configure the Cypress plugin like this:
386
395
 
387
396
  ```json
@@ -398,7 +407,7 @@ In case a plugin causes issues, it can be disabled by using `false` as its value
398
407
 
399
408
  ### Create a new plugin
400
409
 
401
- Getting false positives because a plugin is missing? Want to help out? Please read more at [writing a plugin][12]. This
410
+ Getting false positives because a plugin is missing? Want to help out? Please read more at [writing a plugin][11]. This
402
411
  guide also contains more details if you want to learn more about plugins and why they are useful.
403
412
 
404
413
  ## Compilers
@@ -421,7 +430,7 @@ export default {
421
430
  };
422
431
  ```
423
432
 
424
- Read [Compilers][13] for more details and examples.
433
+ Read [Compilers][12] for more details and examples.
425
434
 
426
435
  ## Production Mode
427
436
 
@@ -498,7 +507,7 @@ When the provided built-in reporters are not sufficient, a custom reporter can b
498
507
  Pass `--reporter ./my-reporter` from the command-line. The data can then be used to write issues to `stdout`, a JSON or
499
508
  CSV file, or sent to a service.
500
509
 
501
- Find more details and ideas in [custom reporters][14].
510
+ Find more details and ideas in [custom reporters][13].
502
511
 
503
512
  ## Public exports
504
513
 
@@ -547,34 +556,33 @@ When unused dependencies are related to dependencies having a Knip [plugin][1],
547
556
  for that dependency are at custom locations. The default values are at the plugin's documentation, and can be overridden
548
557
  to match the custom location(s).
549
558
 
550
- When the dependencies don't have a Knip plugin yet, please file an issue or [create a new plugin][15].
559
+ When the dependencies don't have a Knip plugin yet, please file an issue or [create a new plugin][14].
551
560
 
552
561
  #### Too many unused exports
553
562
 
554
- When the project is a library and the exports are meant to be used by consumers of the library, there are two options:
563
+ Unused exports of `entry` files are not reported. When exports of other files are marked as unused, because they are
564
+ meant to be used by consumers of the library, there are a few options:
555
565
 
556
- 1. By default, unused exports of `entry` files are not reported. You could re-export from an existing entry file, or
557
- add the containing file to the `entry` array in the configuration.
558
- 2. The exported values or types can be marked [using the JSDoc `@public` tag][16]
566
+ 1. Add the containing file to the `entry` array in the configuration.
567
+ 2. Re-export from an existing entry file.
568
+ 3. Mark the exported value or type [using the JSDoc `@public` tag][15].
559
569
 
560
570
  ### How to start using Knip in CI while having too many issues to sort out?
561
571
 
562
572
  Eventually this type of QA only really works when it's tied to an automated workflow. But with too many issues to
563
573
  resolve this might not be feasible right away, especially in existing larger codebase. Here are a few options that may
564
- help:
574
+ help in the meantime:
565
575
 
566
576
  - Use `--no-exit-code` for exit code 0 in CI.
567
577
  - Use `--include` (or `--exclude`) to report only the issue types that have little or no errors.
568
- - Use separate Knip commands to analyze e.g. unused `--dependencies` and/or `--exports`.
569
- - Use [ignore patterns][10] to filter out the most problematic areas.
570
-
571
- All of this is hiding problems, so please make sure to plan for fixing them and/or open issues here for false positives.
578
+ - Use separate Knip commands to analyze e.g. only `--dependencies` or `--exports`.
579
+ - Use [ignore patterns][9] to filter out the most problematic areas.
572
580
 
573
581
  ## Comparison
574
582
 
575
583
  This table is an ongoing comparison. Based on their docs (please report any mistakes):
576
584
 
577
- | Feature | **knip** | [depcheck][17] | [unimported][18] | [ts-unused-exports][19] | [ts-prune][20] |
585
+ | Feature | **knip** | [depcheck][16] | [unimported][17] | [ts-unused-exports][18] | [ts-prune][19] |
578
586
  | :---------------------- | :------: | :------------: | :--------------: | :---------------------: | :------------: |
579
587
  | Unused files | ✅ | - | ✅ | - | - |
580
588
  | Unused dependencies | ✅ | ✅ | ✅ | - | - |
@@ -610,7 +618,7 @@ The following commands are similar:
610
618
  unimported
611
619
  knip --production --dependencies --include files
612
620
 
613
- Also see [production mode][21].
621
+ Also see [production mode][20].
614
622
 
615
623
  ### ts-unused-exports
616
624
 
@@ -632,19 +640,25 @@ The following commands are similar:
632
640
 
633
641
  Many thanks to some of the early adopters of Knip:
634
642
 
635
- - [Block Protocol][22]
636
- - [DeepmergeTS][23]
637
- - [eslint-plugin-functional][24]
638
- - [freeCodeCamp.org][25]
639
- - [is-immutable-type][26]
640
- - [release-it][27]
641
- - [Template TypeScript Node Package][28]
643
+ - [Block Protocol][21]
644
+ - [DeepmergeTS][22]
645
+ - [eslint-plugin-functional][23]
646
+ - [freeCodeCamp.org][24]
647
+ - [is-immutable-type][25]
648
+ - [release-it][26]
649
+ - [Template TypeScript Node Package][27]
642
650
 
643
651
  ## Knip?!
644
652
 
645
653
  Knip is Dutch for a "cut". A Dutch expression is "to be ge**knip**t for something", which means to be perfectly suited
646
654
  for the job. I'm motivated to make knip perfectly suited for the job of cutting projects to perfection! ✂️
647
655
 
656
+ ## Contributors
657
+
658
+ Special thanks to the wonderful people who have contributed to this project:
659
+
660
+ [![Contributors][29]][28]
661
+
648
662
  [1]: #workspaces-monorepos
649
663
  [2]: #plugins
650
664
  [3]: #compilers
@@ -652,27 +666,28 @@ for the job. I'm motivated to make knip perfectly suited for the job of cutting
652
666
  [5]: #custom-reporters
653
667
  [6]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
654
668
  [7]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
655
- [8]: ./docs/release-notes-v2.md
656
- [9]: #faq
657
- [10]: #ignore
658
- [11]: https://nx.dev/concepts/integrated-vs-package-based
659
- [12]: ./docs/writing-a-plugin.md
660
- [13]: ./docs/compilers.md
661
- [14]: ./docs/custom-reporters.md
662
- [15]: #create-a-new-plugin
663
- [16]: #public-exports
664
- [17]: https://github.com/depcheck/depcheck
665
- [18]: https://github.com/smeijer/unimported
666
- [19]: https://github.com/pzavolinsky/ts-unused-exports
667
- [20]: https://github.com/nadeesha/ts-prune
668
- [21]: #production-mode
669
- [22]: https://github.com/blockprotocol/blockprotocol
670
- [23]: https://github.com/RebeccaStevens/deepmerge-ts
671
- [24]: https://github.com/eslint-functional/eslint-plugin-functional
672
- [25]: https://github.com/freeCodeCamp/freeCodeCamp
673
- [26]: https://github.com/RebeccaStevens/is-immutable-type
674
- [27]: https://github.com/release-it/release-it
675
- [28]: https://github.com/JoshuaKGoldberg/template-typescript-node-package
669
+ [8]: #faq
670
+ [9]: #ignore
671
+ [10]: https://nx.dev/concepts/integrated-vs-package-based
672
+ [11]: ./docs/writing-a-plugin.md
673
+ [12]: ./docs/compilers.md
674
+ [13]: ./docs/custom-reporters.md
675
+ [14]: #create-a-new-plugin
676
+ [15]: #public-exports
677
+ [16]: https://github.com/depcheck/depcheck
678
+ [17]: https://github.com/smeijer/unimported
679
+ [18]: https://github.com/pzavolinsky/ts-unused-exports
680
+ [19]: https://github.com/nadeesha/ts-prune
681
+ [20]: #production-mode
682
+ [21]: https://github.com/blockprotocol/blockprotocol
683
+ [22]: https://github.com/RebeccaStevens/deepmerge-ts
684
+ [23]: https://github.com/eslint-functional/eslint-plugin-functional
685
+ [24]: https://github.com/freeCodeCamp/freeCodeCamp
686
+ [25]: https://github.com/RebeccaStevens/is-immutable-type
687
+ [26]: https://github.com/release-it/release-it
688
+ [27]: https://github.com/JoshuaKGoldberg/template-typescript-node-package
689
+ [28]: https://github.com/webpro/knip/graphs/contributors
690
+ [29]: https://contrib.rocks/image?repo=webpro/knip
676
691
  [plugin-ava]: ./src/plugins/ava
677
692
  [plugin-babel]: ./src/plugins/babel
678
693
  [plugin-capacitor]: ./src/plugins/capacitor
@@ -72,6 +72,24 @@ export class DependencyDeputy {
72
72
  return true;
73
73
  const workspaceNames = this.isStrict ? [workspace.name] : [workspace.name, ...[...workspace.ancestors].reverse()];
74
74
  const closestWorkspaceName = workspaceNames.find(name => this.isInDependencies(name, packageName));
75
+ if (packageName.startsWith('bin:')) {
76
+ const pkg = packageName.replace(/^bin:/, '');
77
+ if (IGNORED_GLOBAL_BINARIES.includes(pkg))
78
+ return true;
79
+ if (this.getWorkspaceManifest(workspace.name)?.ignoreBinaries.includes(pkg))
80
+ return true;
81
+ for (const name of workspaceNames) {
82
+ const binaries = this.getInstalledBinaries(name);
83
+ if (binaries?.has(pkg)) {
84
+ const dependencies = binaries.get(pkg);
85
+ if (dependencies?.size) {
86
+ dependencies.forEach(dependency => this.addReferencedDependency(name, dependency));
87
+ return true;
88
+ }
89
+ }
90
+ }
91
+ return false;
92
+ }
75
93
  if (this.getWorkspaceManifest(workspace.name)?.ignoreDependencies.includes(packageName))
76
94
  return true;
77
95
  const typesPackageName = !isDefinitelyTyped(packageName) && getDefinitelyTypedFor(packageName);
@@ -81,20 +99,6 @@ export class DependencyDeputy {
81
99
  closestWorkspaceNameForTypes && this.addReferencedDependency(closestWorkspaceNameForTypes, typesPackageName);
82
100
  return true;
83
101
  }
84
- if (IGNORED_GLOBAL_BINARIES.includes(packageName))
85
- return true;
86
- if (this.getWorkspaceManifest(workspace.name)?.ignoreBinaries.includes(packageName))
87
- return true;
88
- for (const name of workspaceNames) {
89
- const binaries = this.getInstalledBinaries(name);
90
- if (binaries?.has(packageName)) {
91
- const dependencies = binaries.get(packageName);
92
- if (dependencies?.size) {
93
- dependencies.forEach(dependency => this.addReferencedDependency(name, dependency));
94
- return true;
95
- }
96
- }
97
- }
98
102
  return false;
99
103
  }
100
104
  isInDependencies(workspaceName, packageName) {
@@ -207,7 +207,7 @@ export class WorkspaceWorker {
207
207
  if (!pluginConfig)
208
208
  continue;
209
209
  const patterns = this.getConfigurationFilePatterns(pluginName);
210
- const configFilePaths = await _pureGlob({ patterns, cwd, ignore });
210
+ const configFilePaths = await _pureGlob({ patterns, cwd, ignore, gitignore: false });
211
211
  debugLogArray(`Found ${plugin.NAME} config file paths`, configFilePaths);
212
212
  if (configFilePaths.length === 0)
213
213
  continue;
@@ -13,7 +13,7 @@ const getDependenciesFromScripts = (npmScripts, options = {}) => {
13
13
  return identifier;
14
14
  const packageName = getPackageNameFromModuleSpecifier(stripBinary(identifier));
15
15
  if (!packageName.startsWith('.'))
16
- return packageName;
16
+ return `bin:${packageName}`;
17
17
  }));
18
18
  };
19
19
  export const _getDependenciesFromScripts = timerify(getDependenciesFromScripts);
@@ -5,6 +5,7 @@ const withPositional = parsed => [parsed._[0], parsed.require].flat();
5
5
  const withoutPositional = parsed => [parsed.require].flat();
6
6
  const argFilters = {
7
7
  'babel-node': withPositional,
8
+ esbuild: withPositional,
8
9
  nodemon: withPositional,
9
10
  'ts-node': withPositional,
10
11
  zx: withPositional,
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { debugLogObject, debugLogArray, debugLog } from './util/debug.js';
10
10
  import { LoaderError } from './util/errors.js';
11
11
  import { findFile } from './util/fs.js';
12
12
  import { _glob } from './util/glob.js';
13
- import { getPackageNameFromFilePath, getPackageNameFromModuleSpecifier } from './util/modules.js';
13
+ import { getEntryPathFromManifest, getPackageNameFromFilePath, getPackageNameFromModuleSpecifier, } from './util/modules.js';
14
14
  import { dirname, isInNodeModules, join, isInternal, isAbsolute } from './util/path.js';
15
15
  import { _resolveSpecifier, _tryResolve } from './util/require.js';
16
16
  import { _require } from './util/require.js';
@@ -95,6 +95,9 @@ export const main = async (unresolvedConfiguration) => {
95
95
  });
96
96
  await worker.init();
97
97
  const sharedGlobOptions = { cwd, workingDir: dir, gitignore, ignore: worker.getIgnorePatterns() };
98
+ const entryPathsFromManifest = await getEntryPathFromManifest(dir, manifest);
99
+ debugLogArray(`Found entry paths from manifest (${name})`, entryPathsFromManifest);
100
+ principal.addEntryPaths(entryPathsFromManifest);
98
101
  if (isProduction) {
99
102
  {
100
103
  const patterns = worker.getProductionEntryFilePatterns();
@@ -48,26 +48,29 @@ export const getDependenciesDeep = async (configFilePath, dependencies = new Set
48
48
  return dependencies;
49
49
  };
50
50
  const resolvePackageName = (namespace, pluginName) => {
51
- return pluginName.startsWith('@')
52
- ? pluginName.includes('/')
53
- ? pluginName.replace(/\//, `/${namespace}-`)
54
- : `${pluginName}/${namespace}`
55
- : `${namespace}-${pluginName}`;
51
+ return pluginName.includes(namespace + '-')
52
+ ? pluginName
53
+ : pluginName.startsWith('@')
54
+ ? pluginName.includes('/')
55
+ ? pluginName.replace(/\//, `/${namespace}-`)
56
+ : `${pluginName}/${namespace}`
57
+ : `${namespace}-${pluginName}`;
56
58
  };
57
59
  export const resolvePluginPackageName = (pluginName) => resolvePackageName('eslint-plugin', pluginName);
58
60
  const resolveExtendsSpecifier = (specifier) => {
59
61
  if (isInternal(specifier))
60
62
  return;
61
- if (specifier.includes(':')) {
62
- const strippedSpecifier = specifier.replace(/(^plugin:|:.+$)/, '').replace(/\/(eslint-)?recommended$/, '');
63
- const pluginName = getPackageNameFromModuleSpecifier(strippedSpecifier);
64
- if (pluginName === 'eslint')
65
- return;
66
- if (pluginName.includes('eslint-'))
67
- return pluginName;
68
- return resolvePackageName('eslint-plugin', pluginName);
69
- }
70
- return specifier.includes('eslint') ? specifier : resolvePackageName('eslint-config', specifier);
63
+ if (/\/eslint-(config|plugin)/.test(specifier))
64
+ return specifier;
65
+ const strippedSpecifier = specifier
66
+ .replace(/(^plugin:|:.+$)/, '')
67
+ .replace(/\/(eslint-)?(recommended.*|strict|all)$/, '');
68
+ if (/eslint-(config|plugin)-/.test(strippedSpecifier))
69
+ return strippedSpecifier;
70
+ const pluginName = getPackageNameFromModuleSpecifier(strippedSpecifier);
71
+ if (pluginName === 'eslint')
72
+ return;
73
+ return resolvePackageName(specifier.startsWith('plugin:') ? 'eslint-plugin' : 'eslint-config', pluginName);
71
74
  };
72
75
  const getImportPluginDependencies = (settings) => {
73
76
  const knownKeys = ['typescript'];
@@ -7,13 +7,13 @@ interface BaseGlobOptions {
7
7
  cwd: string;
8
8
  patterns: string[];
9
9
  ignore?: string[];
10
+ gitignore?: boolean;
10
11
  }
11
12
  interface GlobOptions extends BaseGlobOptions {
12
13
  workingDir?: string;
13
- gitignore?: boolean;
14
14
  }
15
15
  export declare const _glob: ({ cwd, workingDir, patterns, ignore, gitignore }: GlobOptions) => Promise<string[]>;
16
- export declare const _pureGlob: ({ cwd, patterns, ignore }: BaseGlobOptions) => Promise<string[]>;
16
+ export declare const _pureGlob: ({ cwd, patterns, ignore, gitignore }: BaseGlobOptions) => Promise<string[]>;
17
17
  export declare const _firstGlob: ({ cwd, patterns }: BaseGlobOptions) => Promise<string | Buffer | undefined>;
18
18
  export declare const _dirGlob: ({ cwd, patterns }: BaseGlobOptions) => Promise<string[]>;
19
19
  export {};
package/dist/util/glob.js CHANGED
@@ -29,9 +29,10 @@ const glob = async ({ cwd, workingDir = cwd, patterns, ignore = [], gitignore =
29
29
  dot: true,
30
30
  });
31
31
  };
32
- const pureGlob = async ({ cwd, patterns, ignore = [] }) => globby(patterns, {
32
+ const pureGlob = async ({ cwd, patterns, ignore = [], gitignore = true }) => globby(patterns, {
33
33
  cwd,
34
34
  ignore: [...ignore, '**/node_modules/**'],
35
+ gitignore,
35
36
  absolute: true,
36
37
  });
37
38
  const firstGlob = async ({ cwd, patterns }) => {
@@ -1,6 +1,8 @@
1
+ import type { PackageJson } from '@npmcli/package-json';
1
2
  export declare const getPackageNameFromModuleSpecifier: (moduleSpecifier: string) => string;
2
3
  export declare const getPackageNameFromFilePath: (value: string) => string;
3
4
  export declare const stripBinary: (command: string) => string;
4
5
  export declare const isDefinitelyTyped: (packageName: string) => boolean;
5
6
  export declare const getDefinitelyTypedFor: (packageName: string) => string;
6
7
  export declare const getPackageFromDefinitelyTyped: (typedDependency: string) => string;
8
+ export declare const getEntryPathFromManifest: (dir: string, manifest: PackageJson) => Promise<string[]>;
@@ -1,3 +1,5 @@
1
+ import { _pureGlob } from './glob.js';
2
+ import { getStringValues } from './object.js';
1
3
  import { toPosix } from './path.js';
2
4
  export const getPackageNameFromModuleSpecifier = (moduleSpecifier) => {
3
5
  const parts = moduleSpecifier.split('/').slice(0, 2);
@@ -28,3 +30,24 @@ export const getPackageFromDefinitelyTyped = (typedDependency) => {
28
30
  }
29
31
  return typedDependency;
30
32
  };
33
+ export const getEntryPathFromManifest = (dir, manifest) => {
34
+ const { main, bin, exports } = manifest;
35
+ const entryPaths = new Set();
36
+ if (typeof main === 'string')
37
+ entryPaths.add(main);
38
+ if (bin) {
39
+ if (typeof bin === 'string')
40
+ entryPaths.add(bin);
41
+ if (typeof bin === 'object')
42
+ Object.values(bin).forEach(bin => entryPaths.add(bin));
43
+ }
44
+ if (exports) {
45
+ if (typeof exports === 'string') {
46
+ entryPaths.add(exports);
47
+ }
48
+ else {
49
+ getStringValues(exports).forEach(item => entryPaths.add(item));
50
+ }
51
+ }
52
+ return _pureGlob({ cwd: dir, patterns: Array.from(entryPaths) });
53
+ };
@@ -1 +1,2 @@
1
1
  export declare const getValuesByKeyDeep: (obj: any, key: string) => unknown[];
2
+ export declare const getStringValues: (obj: any) => string[];
@@ -13,3 +13,17 @@ export const getValuesByKeyDeep = (obj, key) => {
13
13
  }
14
14
  return objects;
15
15
  };
16
+ export const getStringValues = (obj) => {
17
+ let values = [];
18
+ for (const prop in obj) {
19
+ if (obj[prop]) {
20
+ if (typeof obj[prop] === 'string') {
21
+ values.push(obj[prop]);
22
+ }
23
+ else if (typeof obj[prop] === 'object') {
24
+ values = values.concat(getStringValues(obj[prop]));
25
+ }
26
+ }
27
+ }
28
+ return values;
29
+ };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.2.4";
1
+ export declare const version = "2.3.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.2.4';
1
+ export const version = '2.3.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.2.4",
3
+ "version": "2.3.1",
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",