knip 2.2.0 → 2.2.2

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
@@ -20,7 +20,7 @@ The dots don't connect themselves. This is where Knip comes in:
20
20
  - [x] Built-in support for [workspaces (monorepos)][1]
21
21
  - [x] Growing list of [built-in plugins][2]
22
22
  - [x] Use [compilers][3] to include other file types (e.g. `.mdx`, `.vue`, `.svelte`)
23
- - [x] Checks npm scripts for used and unlisted dependencies
23
+ - [x] Finds binaries and dependencies in npm scripts, and a lot more locations
24
24
  - [x] Finds unused members of classes and enums
25
25
  - [x] Finds duplicate exports
26
26
  - [x] Supports any combination of JavaScript and TypeScript
@@ -237,9 +237,9 @@ As always, make sure to backup files or use Git before deleting files or making
237
237
 
238
238
  Workspaces and monorepos are handled out-of-the-box by Knip. Every workspace is part of the analysis.
239
239
 
240
- Here's an example configuration with some custom `entry` and `project` patterns:
240
+ Here's an example `knip.json` configuration with some custom `entry` and `project` patterns:
241
241
 
242
- ```jsonc
242
+ ```json
243
243
  {
244
244
  "workspaces": {
245
245
  ".": {
@@ -280,6 +280,9 @@ Here's some example output when running Knip in a workspace:
280
280
 
281
281
  Use `--debug` to get more verbose output.
282
282
 
283
+ Use `ignoreBinaries` and `ignoreDependencies` at the root of `knip.json` for global effect, or inside any workspace
284
+ config for local effect.
285
+
283
286
  ## Plugins
284
287
 
285
288
  Plugins tell Knip where to look for configuration and entry files, and if necessary have a custom dependency finder.
@@ -439,10 +442,9 @@ use the `--production` flag. Here's an example:
439
442
  Here's what's included in production mode analysis:
440
443
 
441
444
  - Only `entry` and `project` patterns suffixed with `!`.
442
- - Only `entry` patterns from plugins exported as `PRODUCTION_ENTRY_FILE_PATTERNS` (such as Next.js and Gatsby).
443
- - Only the `postinstall` and `start` script (e.g. not the `test` or other npm scripts in `package.json`).
444
- - Only `exports`, `nsExports` and `classMembers` are included in the report (`types`, `nsTypes`, `enumMembers` are
445
- ignored).
445
+ - Only production `entry` file patterns exported for plugins (such as Next.js and Gatsby).
446
+ - Only the `start` and `postinstall` scripts (e.g. not the `test` or other npm scripts in `package.json`).
447
+ - Only unused `exports`, `nsExports` and `classMembers` are reported (not `types`, `nsTypes`, `enumMembers`).
446
448
 
447
449
  ### Strict
448
450
 
@@ -587,7 +589,7 @@ This table is an ongoing comparison. Based on their docs (please report any mist
587
589
  | Custom reporters | ✅ | - | - | - | - |
588
590
  | JavaScript support | ✅ | ✅ | ✅ | - | - |
589
591
  | Configure entry files | ✅ | ❌ | ✅ | ❌ | ❌ |
590
- | [Support workspaces][1] | ✅ | ❌ | ❌ | - | - |
592
+ | [Workspaces][1] | ✅ | ❌ | ❌ | - | - |
591
593
  | ESLint plugin available | - | - | - | ✅ | - |
592
594
 
593
595
  ✅ = Supported, ❌ = Not supported, - = Out of scope
package/dist/index.js CHANGED
@@ -283,7 +283,7 @@ export const main = async (unresolvedConfiguration) => {
283
283
  if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
284
284
  collector.addIssue({ type: 'types', filePath, symbol, symbolType: exportedItem.type });
285
285
  }
286
- else {
286
+ else if (!importedModule.isDynamic || !principal.hasExternalReferences(filePath, exportedItem)) {
287
287
  collector.addIssue({ type: 'exports', filePath, symbol });
288
288
  }
289
289
  }
@@ -8,6 +8,7 @@ export type ImportedModule = {
8
8
  isStar: boolean;
9
9
  isReExported: boolean;
10
10
  isReExportedBy: Set<string>;
11
+ isDynamic: boolean;
11
12
  };
12
13
  export type Imports = Map<FilePath, ImportedModule>;
13
14
  export {};
@@ -10,6 +10,7 @@ export type AddImportOptions = {
10
10
  specifier: string;
11
11
  symbol?: ts.Symbol;
12
12
  identifier?: string;
13
+ isDynamic?: boolean;
13
14
  };
14
15
  export type AddExportOptions = ExportItem & {
15
16
  identifier: string;
@@ -19,7 +19,7 @@ export const getImportsAndExports = (sourceFile, options) => {
19
19
  const scripts = new Set();
20
20
  const importedInternalSymbols = new Map();
21
21
  const visitors = getVisitors(sourceFile);
22
- const addInternalImport = ({ identifier = '__anonymous', specifier, symbol, filePath, }) => {
22
+ const addInternalImport = ({ identifier, specifier, symbol, filePath, isDynamic }) => {
23
23
  const isStar = identifier === '*';
24
24
  const isReExported = Boolean(isStar && !symbol);
25
25
  if (!internalImports.has(filePath)) {
@@ -29,6 +29,7 @@ export const getImportsAndExports = (sourceFile, options) => {
29
29
  isReExported,
30
30
  isReExportedBy: new Set(),
31
31
  symbols: new Set(),
32
+ isDynamic,
32
33
  });
33
34
  }
34
35
  const internalImport = internalImports.get(filePath);
@@ -36,17 +37,17 @@ export const getImportsAndExports = (sourceFile, options) => {
36
37
  internalImport.isReExported = isReExported;
37
38
  internalImport.isReExportedBy.add(sourceFile.fileName);
38
39
  }
39
- if (isStar) {
40
+ if (isStar)
40
41
  internalImport.isStar = isStar;
41
- }
42
- if (!isStar) {
42
+ if (!isStar)
43
43
  internalImport.symbols.add(identifier);
44
- }
45
44
  if (isStar && symbol) {
46
45
  importedInternalSymbols.set(symbol, filePath);
47
46
  }
47
+ if (isDynamic)
48
+ internalImport.isDynamic = isDynamic;
48
49
  };
49
- const addImport = ({ specifier, symbol, identifier = '__anonymous' }) => {
50
+ const addImport = ({ specifier, symbol, identifier = '__anonymous', isDynamic = false }) => {
50
51
  if (isBuiltin(specifier))
51
52
  return;
52
53
  const module = sourceFile.resolvedModules?.get(specifier, undefined);
@@ -55,7 +56,7 @@ export const getImportsAndExports = (sourceFile, options) => {
55
56
  if (filePath) {
56
57
  if (module.resolvedModule.isExternalLibraryImport) {
57
58
  if (!isInNodeModules(filePath)) {
58
- addInternalImport({ identifier, specifier, symbol, filePath });
59
+ addInternalImport({ identifier, specifier, symbol, filePath, isDynamic });
59
60
  }
60
61
  else if (isDeclarationFileExtension(module.resolvedModule.extension)) {
61
62
  externalImports.add(specifier);
@@ -65,7 +66,7 @@ export const getImportsAndExports = (sourceFile, options) => {
65
66
  }
66
67
  }
67
68
  else {
68
- addInternalImport({ identifier, specifier, symbol, filePath });
69
+ addInternalImport({ identifier, specifier, symbol, filePath, isDynamic });
69
70
  }
70
71
  }
71
72
  }
@@ -1,28 +1,25 @@
1
1
  import ts from 'typescript';
2
- import { isImportCall, isAccessExpression, getAccessExpressionName, isVariableDeclarationList, findDescendants, } from '../../ast-helpers.js';
2
+ import { isImportCall, isAccessExpression, isVariableDeclarationList, findDescendants } from '../../ast-helpers.js';
3
3
  import { importVisitor as visit } from '../index.js';
4
4
  export default visit(() => true, node => {
5
5
  if (isImportCall(node)) {
6
6
  if (node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {
7
7
  const specifier = node.arguments[0].text;
8
- let _node = node.parent;
9
- while (_node) {
10
- if (_node.parent && ts.isCallExpression(_node.parent)) {
11
- return { specifier, identifier: 'default' };
12
- }
13
- if (isAccessExpression(_node)) {
14
- const identifier = getAccessExpressionName(_node);
15
- const isPromiseLike = identifier === 'then';
16
- const symbol = isPromiseLike ? 'default' : identifier;
17
- return { identifier: symbol, specifier };
18
- }
19
- if (isVariableDeclarationList(_node)) {
20
- return findDescendants(_node, ts.isVariableDeclaration).flatMap(variableDeclaration => {
8
+ if (isAccessExpression(node.parent)) {
9
+ return { specifier, isDynamic: true };
10
+ }
11
+ let _ancestor = node.parent?.parent?.parent;
12
+ if (_ancestor && isAccessExpression(_ancestor)) {
13
+ return { specifier, isDynamic: true };
14
+ }
15
+ while (_ancestor) {
16
+ if (_ancestor && isVariableDeclarationList(_ancestor)) {
17
+ return findDescendants(_ancestor, ts.isVariableDeclaration).flatMap(variableDeclaration => {
21
18
  if (ts.isIdentifier(variableDeclaration.name)) {
22
19
  return { identifier: 'default', specifier };
23
20
  }
24
21
  else {
25
- const binds = findDescendants(variableDeclaration, _node => ts.isBindingElement(_node));
22
+ const binds = findDescendants(variableDeclaration, _node => ts.isBindingElement(_node) && ts.isIdentifier(_node.name));
26
23
  return binds.flatMap(element => {
27
24
  const symbol = element.propertyName?.getText() || element.name.getText();
28
25
  return { identifier: symbol, specifier };
@@ -30,7 +27,7 @@ export default visit(() => true, node => {
30
27
  }
31
28
  });
32
29
  }
33
- _node = _node.parent;
30
+ _ancestor = _ancestor.parent;
34
31
  }
35
32
  return { specifier };
36
33
  }
@@ -1,8 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import { isRequireCall, findAncestor, findDescendants } from '../../ast-helpers.js';
3
- import { isJS } from '../helpers.js';
4
3
  import { importVisitor as visit } from '../index.js';
5
- export default visit(isJS, node => {
4
+ export default visit(() => true, node => {
6
5
  if (isRequireCall(node)) {
7
6
  if (ts.isStringLiteralLike(node.arguments[0])) {
8
7
  const specifier = node.arguments[0].text;
@@ -1,14 +1,12 @@
1
1
  import ts from 'typescript';
2
2
  import { isRequireResolveCall } from '../../ast-helpers.js';
3
- import { isJS } from '../helpers.js';
4
3
  import { importVisitor as visit } from '../index.js';
5
- export default visit(isJS, node => {
4
+ export default visit(() => true, node => {
6
5
  if (isRequireResolveCall(node)) {
7
6
  if (node.arguments[0] && ts.isStringLiteralLike(node.arguments[0])) {
8
7
  const specifier = node.arguments[0].text;
9
- if (specifier) {
8
+ if (specifier)
10
9
  return { specifier };
11
- }
12
10
  }
13
11
  }
14
12
  });
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.2.0";
1
+ export declare const version = "2.2.2";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.2.0';
1
+ export const version = '2.2.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
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",
@@ -66,14 +66,14 @@
66
66
  "@types/js-yaml": "4.0.5",
67
67
  "@types/micromatch": "4.0.2",
68
68
  "@types/minimist": "1.2.2",
69
- "@types/node": "18.15.10",
69
+ "@types/node": "18.15.11",
70
70
  "@types/npmcli__map-workspaces": "3.0.0",
71
- "@types/webpack": "5.28.0",
72
- "@typescript-eslint/eslint-plugin": "5.56.0",
73
- "@typescript-eslint/parser": "5.56.0",
71
+ "@types/webpack": "5.28.1",
72
+ "@typescript-eslint/eslint-plugin": "5.57.0",
73
+ "@typescript-eslint/parser": "5.57.0",
74
74
  "c8": "7.13.0",
75
- "eslint": "8.36.0",
76
- "eslint-import-resolver-typescript": "3.5.3",
75
+ "eslint": "8.37.0",
76
+ "eslint-import-resolver-typescript": "3.5.4",
77
77
  "eslint-plugin-import": "2.27.5",
78
78
  "globstar": "1.0.0",
79
79
  "prettier": "2.8.7",
@@ -81,7 +81,7 @@
81
81
  "remark-cli": "11.0.0",
82
82
  "remark-preset-webpro": "0.0.2",
83
83
  "tsx": "3.12.6",
84
- "type-fest": "3.7.1"
84
+ "type-fest": "3.7.2"
85
85
  },
86
86
  "engines": {
87
87
  "node": ">=16.17.0 <17 || >=18.6.0"