knip 2.19.5 → 2.19.7

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.
@@ -58,6 +58,7 @@ export declare class ProjectPrincipal {
58
58
  };
59
59
  findUnusedMembers(filePath: string, members: ExportItemMember[]): string[];
60
60
  private findReferences;
61
- isPublicExport(exportedItem: ExportItem): ts.JSDocPublicTag | undefined;
61
+ isPublicExport(exportedItem: ExportItem): boolean;
62
+ getJSDocTags(exportedItem: ExportItem): string[];
62
63
  }
63
64
  export {};
@@ -195,6 +195,10 @@ export class ProjectPrincipal {
195
195
  return this.backend.lsFindReferences(filePath, node.getStart()) ?? [];
196
196
  }
197
197
  isPublicExport(exportedItem) {
198
- return ts.getJSDocPublicTag(exportedItem.node);
198
+ const tags = this.getJSDocTags(exportedItem);
199
+ return tags.includes('@public');
200
+ }
201
+ getJSDocTags(exportedItem) {
202
+ return ts.getJSDocTags(exportedItem.node).map(node => node.getText().match(/@\S+/)[0]);
199
203
  }
200
204
  }
@@ -1,51 +1,55 @@
1
- import parse from '@ericcornelissen/bash-parser';
1
+ import Parser from 'tree-sitter';
2
+ import Bash from 'tree-sitter-bash';
2
3
  import { debugLogObject } from '../util/debug.js';
3
4
  import * as FallbackResolver from './resolvers/fallback.js';
4
5
  import * as KnownResolvers from './resolvers/index.js';
5
6
  import { stripBinaryPath } from './util.js';
7
+ const parser = new Parser();
8
+ parser.setLanguage(Bash);
9
+ const getCommandsFromScript = (script) => {
10
+ const tree = parser.parse(script);
11
+ const commands = [];
12
+ const traverse = (node) => {
13
+ switch (node.type) {
14
+ case 'command': {
15
+ const commandNameIndex = node.children.findIndex(node => node.type === 'command_name');
16
+ const command = node.children.slice(commandNameIndex).map(node => node.text);
17
+ commands.push(command);
18
+ break;
19
+ }
20
+ default:
21
+ break;
22
+ }
23
+ for (const child of node.children) {
24
+ traverse(child);
25
+ }
26
+ };
27
+ traverse(tree.rootNode);
28
+ return commands;
29
+ };
6
30
  export const getBinariesFromScript = (script, { cwd, manifest, knownGlobalsOnly = false }) => {
7
31
  if (!script)
8
32
  return [];
9
33
  const fromArgs = (args) => getBinariesFromScript(args.filter(arg => arg !== '--').join(' '), { cwd, manifest });
10
- const getBinariesFromNodes = (nodes) => nodes.flatMap(node => {
11
- switch (node.type) {
12
- case 'Command': {
13
- const binary = node.name?.text ? stripBinaryPath(node.name.text) : node.name?.text;
14
- const commandExpansions = node.prefix?.flatMap(prefix => prefix.expansion?.filter(expansion => expansion.type === 'CommandExpansion') ?? []) ?? [];
15
- if (commandExpansions.length > 0) {
16
- return commandExpansions.flatMap(expansion => getBinariesFromNodes(expansion.commandAST.commands)) ?? [];
17
- }
18
- if (!binary || binary === '.' || binary === 'source')
19
- return [];
20
- if (binary.startsWith('-') || binary.startsWith('"') || binary.startsWith('..'))
21
- return [];
22
- if (['bun', 'deno'].includes(binary))
23
- return [];
24
- const args = node.suffix?.map(arg => arg.text) ?? [];
25
- if (['!', 'test'].includes(binary))
26
- return fromArgs(args);
27
- if (binary in KnownResolvers) {
28
- return KnownResolvers[binary].resolve(binary, args, { cwd, manifest, fromArgs });
29
- }
30
- if (knownGlobalsOnly)
31
- return [];
32
- return FallbackResolver.resolve(binary, args, { cwd, manifest, fromArgs });
33
- }
34
- case 'LogicalExpression':
35
- return getBinariesFromNodes([node.left, node.right]);
36
- case 'If':
37
- return getBinariesFromNodes([...node.clause.commands, ...node.then.commands, ...(node.else?.commands ?? [])]);
38
- case 'For':
39
- return getBinariesFromNodes(node.do.commands);
40
- case 'CompoundList':
41
- return getBinariesFromNodes(node.commands);
42
- default:
43
- return [];
34
+ const commands = getCommandsFromScript(script);
35
+ const getBinariesFromCommand = (command) => {
36
+ const [bin, ...args] = command;
37
+ const binary = stripBinaryPath(bin);
38
+ if (!binary || binary === '.' || binary === 'source')
39
+ return [];
40
+ if (binary.startsWith('-') || binary.startsWith('"') || binary.startsWith('..'))
41
+ return [];
42
+ if (['bun', 'deno'].includes(binary))
43
+ return [];
44
+ if (binary in KnownResolvers) {
45
+ return KnownResolvers[binary].resolve(binary, args, { cwd, manifest, fromArgs });
44
46
  }
45
- });
47
+ if (knownGlobalsOnly)
48
+ return [];
49
+ return FallbackResolver.resolve(binary, args, { cwd, manifest, fromArgs });
50
+ };
46
51
  try {
47
- const parsed = parse(script);
48
- return parsed?.commands ? getBinariesFromNodes(parsed.commands) : [];
52
+ return commands.map(getBinariesFromCommand).flat();
49
53
  }
50
54
  catch (error) {
51
55
  debugLogObject('Bash parser error', error);
@@ -1,5 +1,6 @@
1
1
  import parseArgs from 'minimist';
2
2
  import { isInternal } from '../../util/path.js';
3
+ import { stripQuotes } from '../../util/string.js';
3
4
  import { getBinariesFromScript } from '../bash-parser.js';
4
5
  import { argsFrom, stripVersionFromSpecifier, toBinary } from '../util.js';
5
6
  export const resolve = (binary, args, { cwd, fromArgs, manifest }) => {
@@ -10,7 +11,7 @@ export const resolve = (binary, args, { cwd, fromArgs, manifest }) => {
10
11
  const packageSpecifier = parsed._[0];
11
12
  const specifier = packageSpecifier ? stripVersionFromSpecifier(packageSpecifier) : '';
12
13
  const packages = parsed.package ? [parsed.package].flat().map(stripVersionFromSpecifier) : [];
13
- const command = parsed.call ? getBinariesFromScript(parsed.call, { cwd, manifest }) : [];
14
+ const command = parsed.call ? getBinariesFromScript(stripQuotes(parsed.call), { cwd, manifest }) : [];
14
15
  const restArgs = argsFrom(args, packageSpecifier);
15
16
  const dependencies = manifest ? Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }) : [];
16
17
  const isBinary = specifier && !packageSpecifier.includes('@') && !isInternal(specifier) && !dependencies.includes(specifier);
@@ -2,38 +2,54 @@ import parseArgs from 'minimist';
2
2
  import { toBinary } from '../util.js';
3
3
  const commands = [
4
4
  'add',
5
+ 'audit',
6
+ 'bin',
7
+ 'config',
5
8
  'dedupe',
9
+ 'deploy',
6
10
  'dlx',
7
- 'run',
11
+ 'doctor',
12
+ 'env',
13
+ 'fetch',
8
14
  'i',
9
- 'install',
10
- 'up',
11
- 'update',
12
- 'upgrade',
13
- 'remove',
14
- 'rm',
15
- 'uninstall',
16
- 'un',
17
- 'link',
18
- 'ln',
19
- 'unlink',
20
15
  'import',
21
- 'rebuild',
22
- 'rb',
23
- 'prune',
24
- 'fetch',
16
+ 'init',
25
17
  'install-test',
18
+ 'install',
26
19
  'it',
27
- 'patch',
28
- 'patch-commit',
29
- 'audit',
20
+ 'licenses',
21
+ 'link',
30
22
  'list',
23
+ 'ln',
31
24
  'ls',
32
25
  'outdated',
33
- 'why',
34
- 'test',
26
+ 'outdated',
27
+ 'pack',
28
+ 'patch-commit',
29
+ 'patch-remove',
30
+ 'patch',
31
+ 'prune',
32
+ 'publish',
33
+ 'rb',
34
+ 'rebuild',
35
+ 'remove',
36
+ 'rm',
37
+ 'root',
38
+ 'run',
39
+ 'server',
40
+ 'setup',
41
+ 'start',
42
+ 'store',
35
43
  't',
44
+ 'test',
36
45
  'tst',
46
+ 'un',
47
+ 'uninstall',
48
+ 'unlink',
49
+ 'up',
50
+ 'update',
51
+ 'upgrade',
52
+ 'why',
37
53
  ];
38
54
  export const resolve = (_binary, args, { manifest }) => {
39
55
  const scripts = manifest.scripts ? Object.keys(manifest.scripts) : [];
@@ -1,10 +1,11 @@
1
1
  import parseArgs from 'minimist';
2
2
  import { compact } from '../../util/array.js';
3
+ import { stripQuotes } from '../../util/string.js';
3
4
  import { toBinary, tryResolveSpecifiers } from '../util.js';
4
5
  export const resolve = (binary, args, { cwd, fromArgs }) => {
5
6
  const safeArgs = args.filter(arg => arg !== '--watch');
6
7
  const parsed = parseArgs(safeArgs, { alias: { plugin: 'p' } });
7
- const watchers = parsed.watch ? fromArgs(Object.values(parsed.watch)) : [];
8
+ const watchers = parsed.watch ? fromArgs(Object.values(parsed.watch).map(value => stripQuotes(value))) : [];
8
9
  const plugins = parsed.plugin ? tryResolveSpecifiers(cwd, [parsed.plugin].flat()) : [];
9
10
  const configPlugins = parsed.configPlugin ? tryResolveSpecifiers(cwd, [parsed.configPlugin].flat()) : [];
10
11
  return compact([toBinary(binary), ...watchers, ...plugins, ...configPlugins]);
@@ -14,8 +14,8 @@ const commands = [
14
14
  'install',
15
15
  'link',
16
16
  'pack',
17
- 'patch',
18
17
  'patch-commit',
18
+ 'patch',
19
19
  'plugin',
20
20
  'rebuild',
21
21
  'remove',
@@ -25,8 +25,8 @@ const commands = [
25
25
  'unlink',
26
26
  'unplug',
27
27
  'up',
28
- 'upgrade',
29
28
  'upgrade-interactive',
29
+ 'upgrade',
30
30
  'version',
31
31
  'why',
32
32
  'workspace',
package/dist/constants.js CHANGED
@@ -13,6 +13,7 @@ export const IGNORED_GLOBAL_BINARIES = [
13
13
  'cd',
14
14
  'cp',
15
15
  'deno',
16
+ 'dirname',
16
17
  'echo',
17
18
  'exec',
18
19
  'exit',
@@ -28,6 +29,7 @@ export const IGNORED_GLOBAL_BINARIES = [
28
29
  'rm',
29
30
  'sh',
30
31
  'sudo',
32
+ 'test',
31
33
  'true',
32
34
  'yarn',
33
35
  ];
package/dist/index.js CHANGED
@@ -267,7 +267,8 @@ export const main = async (unresolvedConfiguration) => {
267
267
  continue;
268
268
  const importedModule = importedSymbols.get(filePath);
269
269
  for (const [symbol, exportedItem] of exportItems.entries()) {
270
- if (principal.isPublicExport(exportedItem))
270
+ const jsDocTags = principal.getJSDocTags(exportedItem);
271
+ if (jsDocTags.includes('@public') || jsDocTags.includes('@beta'))
271
272
  continue;
272
273
  if (importedModule?.symbols.has(symbol)) {
273
274
  if (importedModule.isReExport && isExportedInEntryFile(importedModule))
@@ -18,7 +18,6 @@ type LiteralLikeElementAccessExpression = ts.ElementAccessExpression & ts.Declar
18
18
  export declare function isModuleExportsAccessExpression(node: ts.Node): node is LiteralLikeElementAccessExpression & {
19
19
  expression: ts.Identifier;
20
20
  };
21
- export declare function stripQuotes(name: string): string;
22
21
  export declare function findAncestor<T>(node: ts.Node | undefined, callback: (element: ts.Node) => boolean | 'STOP'): T | undefined;
23
22
  export declare function findDescendants<T>(node: ts.Node | undefined, callback: (element: ts.Node) => boolean | 'STOP'): T[];
24
23
  export declare const isDeclarationFileExtension: (extension: string) => boolean;
@@ -1,4 +1,5 @@
1
1
  import ts from 'typescript';
2
+ import { stripQuotes } from '../util/string.js';
2
3
  export function isValidImportTypeNode(node) {
3
4
  return ts.isImportTypeNode(node);
4
5
  }
@@ -39,24 +40,6 @@ export function isModuleExportsAccessExpression(node) {
39
40
  node.expression.escapedText === 'module' &&
40
41
  getAccessExpressionName(node) === 'exports');
41
42
  }
42
- export function stripQuotes(name) {
43
- const length = name.length;
44
- if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) {
45
- return name.substring(1, length - 1);
46
- }
47
- return name;
48
- }
49
- var CharacterCodes;
50
- (function (CharacterCodes) {
51
- CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick";
52
- CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
53
- CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote";
54
- })(CharacterCodes || (CharacterCodes = {}));
55
- function isQuoteOrBacktick(charCode) {
56
- return (charCode === CharacterCodes.singleQuote ||
57
- charCode === CharacterCodes.doubleQuote ||
58
- charCode === CharacterCodes.backtick);
59
- }
60
43
  export function findAncestor(node, callback) {
61
44
  node = node?.parent;
62
45
  while (node) {
@@ -1,7 +1,8 @@
1
1
  import ts from 'typescript';
2
2
  import { SymbolType } from '../../../types/issues.js';
3
3
  import { compact } from '../../../util/array.js';
4
- import { isPrivateMember, stripQuotes } from '../../ast-helpers.js';
4
+ import { stripQuotes } from '../../../util/string.js';
5
+ import { isPrivateMember } from '../../ast-helpers.js';
5
6
  import { exportVisitor as visit } from '../index.js';
6
7
  export default visit(() => true, node => {
7
8
  const modifierKinds = node.modifiers?.map(modifier => modifier.kind) ?? [];
@@ -1,6 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import { SymbolType } from '../../../types/issues.js';
3
- import { isModuleExportsAccessExpression, stripQuotes } from '../../ast-helpers.js';
3
+ import { stripQuotes } from '../../../util/string.js';
4
+ import { isModuleExportsAccessExpression } from '../../ast-helpers.js';
4
5
  import { isJS } from '../helpers.js';
5
6
  import { exportVisitor as visit } from '../index.js';
6
7
  export default visit(isJS, node => {
@@ -1,9 +1,11 @@
1
1
  import ts from 'typescript';
2
- import { stripQuotes } from '../../ast-helpers.js';
2
+ import { stripQuotes } from '../../../util/string.js';
3
3
  import { scriptVisitor as visit } from '../index.js';
4
4
  export default visit(sourceFile => sourceFile.statements.some(statementImportsExeca$), node => {
5
- if (ts.isTaggedTemplateExpression(node) && node.tag.getText() === '$') {
6
- return stripQuotes(node.template.getText());
5
+ if (ts.isTaggedTemplateExpression(node)) {
6
+ if (node.tag.getText() === '$' || (ts.isCallExpression(node.tag) && node.tag.expression.getText() === '$')) {
7
+ return stripQuotes(node.template.getText());
8
+ }
7
9
  }
8
10
  });
9
11
  function statementImportsExeca$(node) {
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { stripQuotes } from '../../ast-helpers.js';
2
+ import { stripQuotes } from '../../../util/string.js';
3
3
  import { scriptVisitor as visit } from '../index.js';
4
4
  export default visit(sourceFile => ts.getShebang(sourceFile.text) === '#!/usr/bin/env zx', node => {
5
5
  if (ts.isTaggedTemplateExpression(node) && node.tag.getText() === '$') {
@@ -0,0 +1 @@
1
+ export declare function stripQuotes(name: string): string;
@@ -0,0 +1,18 @@
1
+ export function stripQuotes(name) {
2
+ const length = name.length;
3
+ if (length >= 2 && name.charCodeAt(0) === name.charCodeAt(length - 1) && isQuoteOrBacktick(name.charCodeAt(0))) {
4
+ return name.substring(1, length - 1);
5
+ }
6
+ return name;
7
+ }
8
+ var CharacterCodes;
9
+ (function (CharacterCodes) {
10
+ CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick";
11
+ CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
12
+ CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote";
13
+ })(CharacterCodes || (CharacterCodes = {}));
14
+ function isQuoteOrBacktick(charCode) {
15
+ return (charCode === CharacterCodes.singleQuote ||
16
+ charCode === CharacterCodes.doubleQuote ||
17
+ charCode === CharacterCodes.backtick);
18
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.19.5";
1
+ export declare const version = "2.19.7";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.19.5';
1
+ export const version = '2.19.7';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.19.5",
3
+ "version": "2.19.7",
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",
@@ -42,35 +42,36 @@
42
42
  "schema.json"
43
43
  ],
44
44
  "dependencies": {
45
- "@ericcornelissen/bash-parser": "^0.5.2",
46
45
  "@npmcli/map-workspaces": "^3.0.4",
47
46
  "@snyk/github-codeowners": "^1.1.0",
48
47
  "chalk": "^5.2.0",
49
48
  "easy-table": "^1.2.0",
50
49
  "fast-glob": "^3.2.12",
51
50
  "globby": "^13.1.3",
52
- "jiti": "1.19.1",
51
+ "jiti": "^1.19.3",
53
52
  "js-yaml": "^4.1.0",
54
53
  "micromatch": "^4.0.5",
55
54
  "minimist": "^1.2.8",
56
55
  "pretty-ms": "^8.0.0",
57
56
  "strip-json-comments": "^5.0.0",
58
57
  "summary": "^2.1.0",
58
+ "tree-sitter": "0.20.5",
59
+ "tree-sitter-bash": "0.20.0",
59
60
  "typescript": "^5.0.2",
60
- "zod": "^3.20.6",
61
- "zod-validation-error": "1.3.1"
61
+ "zod": "^3.22.2",
62
+ "zod-validation-error": "^1.5.0"
62
63
  },
63
64
  "devDependencies": {
64
65
  "@jest/types": "29.6.1",
65
66
  "@npmcli/package-json": "5.0.0",
66
67
  "@release-it/bumper": "5.1.0",
67
68
  "@swc/cli": "0.1.62",
68
- "@swc/core": "1.3.77",
69
+ "@swc/core": "1.3.78",
69
70
  "@types/eslint": "8.44.2",
70
71
  "@types/js-yaml": "4.0.5",
71
72
  "@types/micromatch": "4.0.2",
72
73
  "@types/minimist": "1.2.2",
73
- "@types/node": "20.5.0",
74
+ "@types/node": "20.5.1",
74
75
  "@types/npmcli__map-workspaces": "3.0.1",
75
76
  "@types/webpack": "5.28.1",
76
77
  "@typescript-eslint/eslint-plugin": "6.4.0",
@@ -78,7 +79,7 @@
78
79
  "c8": "8.0.1",
79
80
  "eslint": "8.47.0",
80
81
  "eslint-import-resolver-typescript": "3.6.0",
81
- "eslint-plugin-import": "2.28.0",
82
+ "eslint-plugin-import": "2.28.1",
82
83
  "eslint-plugin-n": "16.0.1",
83
84
  "prettier": "3.0.2",
84
85
  "release-it": "16.1.5",