knip 6.12.0 → 6.12.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.
@@ -139,9 +139,10 @@ export class DependencyDeputy {
139
139
  if (packageName === workspace.pkgName)
140
140
  return true;
141
141
  const workspaceNames = this.isStrict ? [workspace.name] : [workspace.name, ...[...workspace.ancestors].reverse()];
142
- const closestWorkspaceName = workspaceNames.find(name => this.isInDependencies(name, packageName, isDevOnly));
142
+ const isDevOrTypeOnly = isDevOnly || isTypeOnly;
143
+ const closestWorkspaceName = workspaceNames.find(name => this.isInDependencies(name, packageName, isDevOrTypeOnly));
143
144
  const typesPackageName = !isDefinitelyTyped(packageName) && getDefinitelyTypedFor(packageName);
144
- const closestWorkspaceNameForTypes = typesPackageName && workspaceNames.find(name => this.isInDependencies(name, typesPackageName, isDevOnly));
145
+ const closestWorkspaceNameForTypes = typesPackageName && workspaceNames.find(name => this.isInDependencies(name, typesPackageName, isDevOrTypeOnly));
145
146
  if (closestWorkspaceNameForTypes && !this.hasTypesIncluded.get(closestWorkspaceNameForTypes)?.has(packageName))
146
147
  this.addReferencedDependency(closestWorkspaceNameForTypes, typesPackageName);
147
148
  if (closestWorkspaceName) {
@@ -1,15 +1,12 @@
1
- import { isFile } from '../../util/fs.js';
1
+ import { hasDependency } from '../../util/plugin.js';
2
2
  const title = 'Vercel';
3
- const enablers = 'This plugin is enabled when a Vercel project configuration file is found in the root folder.';
4
- const entry = ['vercel.{json,js,mjs,cjs,ts,mts}'];
5
- const configFiles = ['vercel.json', 'vercel.js', 'vercel.mjs', 'vercel.cjs', 'vercel.ts', 'vercel.mts'];
6
- const isEnabled = ({ cwd }) => configFiles.some(file => isFile(cwd, file));
7
- const isRootOnly = true;
3
+ const enablers = ['@vercel/config'];
4
+ const entry = ['vercel.{js,mjs,cjs,ts,mts}'];
5
+ const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
8
6
  const plugin = {
9
7
  title,
10
8
  enablers,
11
9
  isEnabled,
12
10
  entry,
13
- isRootOnly,
14
11
  };
15
12
  export default plugin;
@@ -3,6 +3,11 @@ import { addNsValue, addValue } from '../../util/module-graph.js';
3
3
  import { extractEnumMembers, extractNamespaceMembers, getLineAndCol, getStringValue, isStringLiteral, } from './helpers.js';
4
4
  import { EMPTY_TAGS } from './jsdoc.js';
5
5
  const getName = (n) => (n?.type === 'Identifier' ? n.name : undefined);
6
+ const hasExplicitFunctionReturnType = (node) => !!node &&
7
+ (node.type === 'ArrowFunctionExpression' ||
8
+ node.type === 'FunctionExpression' ||
9
+ node.type === 'FunctionDeclaration') &&
10
+ Boolean(node.returnType);
6
11
  export function handleExportNamed(node, s) {
7
12
  if (s.skipExports || s.isInNamespace(node))
8
13
  return;
@@ -34,18 +39,24 @@ export function handleExportNamed(node, s) {
34
39
  const name = p.argument.name;
35
40
  const fix = s.options.isFixExports ? [p.start, p.end, FIX_FLAGS.OBJECT_BINDING] : undefined;
36
41
  s.addExport(name, SYMBOL_TYPE.UNKNOWN, p.argument.start, [], fix, false, s.getJSDocTags(exportStart));
42
+ if (declarator.init)
43
+ s.collectRefsInType(declarator.init, name, false);
37
44
  s.destructuredExports.add(name);
38
45
  }
39
46
  else if (p.value?.type === 'Identifier') {
40
47
  const name = p.value.name;
41
48
  const fix = s.options.isFixExports ? [p.start, p.end, FIX_FLAGS.OBJECT_BINDING] : undefined;
42
49
  s.addExport(name, SYMBOL_TYPE.UNKNOWN, p.value.start, [], fix, false, s.getJSDocTags(exportStart));
50
+ if (declarator.init)
51
+ s.collectRefsInType(declarator.init, name, false);
43
52
  s.destructuredExports.add(name);
44
53
  }
45
54
  else if (p.value?.type === 'AssignmentPattern' && p.value.left?.type === 'Identifier') {
46
55
  const name = p.value.left.name;
47
56
  const fix = s.options.isFixExports ? [p.start, p.end, FIX_FLAGS.OBJECT_BINDING] : undefined;
48
57
  s.addExport(name, SYMBOL_TYPE.UNKNOWN, p.value.left.start, [], fix, false, s.getJSDocTags(exportStart));
58
+ if (declarator.init)
59
+ s.collectRefsInType(declarator.init, name, false);
49
60
  s.destructuredExports.add(name);
50
61
  }
51
62
  }
@@ -55,11 +66,15 @@ export function handleExportNamed(node, s) {
55
66
  if (el?.type === 'Identifier') {
56
67
  const fix = s.options.isFixExports ? [el.start, el.end, FIX_FLAGS.NONE] : undefined;
57
68
  s.addExport(el.name, SYMBOL_TYPE.UNKNOWN, el.start, [], fix, false, s.getJSDocTags(exportStart));
69
+ if (declarator.init)
70
+ s.collectRefsInType(declarator.init, el.name, false);
58
71
  s.destructuredExports.add(el.name);
59
72
  }
60
73
  else if (el?.type === 'RestElement' && el.argument?.type === 'Identifier') {
61
74
  const fix = s.options.isFixExports ? [el.start, el.end, FIX_FLAGS.NONE] : undefined;
62
75
  s.addExport(el.argument.name, SYMBOL_TYPE.UNKNOWN, el.argument.start, [], fix, false, s.getJSDocTags(exportStart));
76
+ if (declarator.init)
77
+ s.collectRefsInType(declarator.init, el.argument.name, false);
63
78
  s.destructuredExports.add(el.argument.name);
64
79
  }
65
80
  }
@@ -111,10 +126,11 @@ export function handleExportNamed(node, s) {
111
126
  findSpreads(declarator.init, [name]);
112
127
  }
113
128
  s.addExport(name, SYMBOL_TYPE.UNKNOWN, declarator.id.start, [], fix, isReExport, jsDocTags);
114
- if (declarator.id.typeAnnotation)
129
+ if (declarator.id.typeAnnotation) {
115
130
  s.collectRefsInType(declarator.id.typeAnnotation, name, true);
116
- if (declarator.init?.type === 'ArrowFunctionExpression' || declarator.init?.type === 'FunctionExpression') {
117
- s.collectRefsInType(declarator.init, name, true);
131
+ }
132
+ else if (declarator.init) {
133
+ s.collectRefsInType(declarator.init, name, hasExplicitFunctionReturnType(declarator.init));
118
134
  }
119
135
  if (!jsDocTags.has(ALIAS_TAG) && declarator.init?.type === 'Identifier') {
120
136
  const initName = declarator.init.name;
@@ -138,11 +154,12 @@ export function handleExportNamed(node, s) {
138
154
  else if ((decl.type === 'FunctionDeclaration' || decl.type === 'TSDeclareFunction') && decl.id) {
139
155
  const fix = s.getFix(exportStart, exportStart + 7);
140
156
  s.addExport(decl.id.name, SYMBOL_TYPE.FUNCTION, decl.id.start, [], fix, false, s.getJSDocTags(exportStart));
141
- s.collectRefsInType(decl, decl.id.name, true);
157
+ s.collectRefsInType(decl, decl.id.name, hasExplicitFunctionReturnType(decl));
142
158
  }
143
159
  else if (decl.type === 'ClassDeclaration' && decl.id) {
144
160
  const fix = s.getFix(exportStart, exportStart + 7);
145
161
  s.addExport(decl.id.name, SYMBOL_TYPE.CLASS, decl.id.start, [], fix, false, s.getJSDocTags(exportStart));
162
+ s.collectRefsInType(decl, decl.id.name, true);
146
163
  }
147
164
  else if (decl.type === 'TSTypeAliasDeclaration') {
148
165
  const fix = s.getTypeFix(exportStart, exportStart + 7);
@@ -216,12 +233,13 @@ export function handleExportDefault(node, s) {
216
233
  if (decl.type === 'FunctionDeclaration') {
217
234
  type = SYMBOL_TYPE.FUNCTION;
218
235
  pos = decl.id?.start ?? decl.start;
219
- s.collectRefsInType(decl, 'default', true);
236
+ s.collectRefsInType(decl, 'default', hasExplicitFunctionReturnType(decl));
220
237
  }
221
238
  else if (decl.type === 'ClassDeclaration') {
222
239
  type = SYMBOL_TYPE.CLASS;
223
240
  pos = decl.id?.start ?? decl.start;
224
241
  members = [];
242
+ s.collectRefsInType(decl, 'default', true);
225
243
  }
226
244
  else if (decl.type === 'TSInterfaceDeclaration') {
227
245
  type = SYMBOL_TYPE.INTERFACE;
@@ -1,4 +1,4 @@
1
- import { Visitor, type Program, type Span } from 'oxc-parser';
1
+ import { Visitor, type Class, type Function as FunctionNode, type Program, type Span, type VariableDeclarator } from 'oxc-parser';
2
2
  import type { PluginVisitorObject } from '../../types/config.ts';
3
3
  import type { GetImportsAndExportsOptions } from '../../types/config.ts';
4
4
  import type { Fix } from '../../types/exports.ts';
@@ -60,6 +60,18 @@ export interface WalkState extends WalkContext {
60
60
  scopeStarts: number[];
61
61
  scopeEnds: number[];
62
62
  shadowScopes: Map<string, [number, number][]>;
63
+ localDeclarations: Map<string, FunctionNode | Class | VariableDeclarator>;
64
+ pendingCallRefs: Array<{
65
+ name: string;
66
+ exportName: string;
67
+ seen: Set<string>;
68
+ }>;
69
+ pendingMemberCallRefs: Array<{
70
+ objectName: string;
71
+ propertyName: string;
72
+ exportName: string;
73
+ seen: Set<string>;
74
+ }>;
63
75
  addExport: (identifier: string, type: SymbolType, pos: number, members: ExportMember[], fix: Fix, isReExport: boolean, jsDocTags: Set<string>) => void;
64
76
  getFix: (start: number, end: number, flags?: number) => Fix;
65
77
  getTypeFix: (start: number, end: number) => Fix;
@@ -1,4 +1,4 @@
1
- import { Visitor } from 'oxc-parser';
1
+ import { Visitor, visitorKeys, } from 'oxc-parser';
2
2
  import { FIX_FLAGS, IMPORT_FLAGS, OPAQUE, SYMBOL_TYPE } from '../../constants.js';
3
3
  import { addValue } from '../../util/module-graph.js';
4
4
  import { isInNodeModules } from '../../util/path.js';
@@ -47,42 +47,79 @@ const _addExport = (identifier, type, pos, members, fix, isReExport, jsDocTags)
47
47
  });
48
48
  }
49
49
  };
50
- const _collectRefsInType = (node, exportName, signatureOnly) => {
51
- if (!node || typeof node !== 'object')
50
+ const _collectRefsInType = (node, exportName, signatureOnly, seen = new Set(), inBody = false) => {
51
+ if (!node)
52
52
  return;
53
- if (node.type === 'TSTypeQuery') {
54
- const name = node.exprName.type === 'Identifier' ? node.exprName.name : undefined;
55
- if (name) {
56
- const refs = state.referencedInExport.get(name);
57
- if (refs)
58
- refs.add(exportName);
59
- else
60
- state.referencedInExport.set(name, new Set([exportName]));
61
- }
53
+ const type = node.type;
54
+ if (!type)
62
55
  return;
56
+ switch (type) {
57
+ case 'TSTypeQuery':
58
+ if (node.exprName?.type === 'Identifier')
59
+ _addRefInExport(node.exprName.name, exportName);
60
+ return;
61
+ case 'TSTypeReference':
62
+ if (node.typeName?.type === 'Identifier')
63
+ _addRefInExport(node.typeName.name, exportName);
64
+ break;
65
+ case 'CallExpression': {
66
+ const callee = node.callee;
67
+ if (callee?.type === 'Identifier') {
68
+ state.pendingCallRefs.push({ name: callee.name, exportName, seen });
69
+ }
70
+ else if (callee?.type === 'MemberExpression' &&
71
+ !callee.computed &&
72
+ callee.object?.type === 'Identifier' &&
73
+ callee.property?.type === 'Identifier') {
74
+ state.pendingMemberCallRefs.push({
75
+ objectName: callee.object.name,
76
+ propertyName: callee.property.name,
77
+ exportName,
78
+ seen,
79
+ });
80
+ }
81
+ if (!inBody) {
82
+ const args = node.arguments;
83
+ if (args) {
84
+ for (const arg of args) {
85
+ if (arg?.type === 'Identifier')
86
+ state.pendingCallRefs.push({ name: arg.name, exportName, seen });
87
+ }
88
+ }
89
+ }
90
+ break;
91
+ }
92
+ case 'FunctionBody':
93
+ case 'BlockStatement':
94
+ if (signatureOnly)
95
+ return;
96
+ break;
97
+ case 'TSAsExpression':
98
+ case 'TSTypeAssertion':
99
+ case 'TSSatisfiesExpression':
100
+ if (inBody) {
101
+ if (node.expression)
102
+ _collectRefsInType(node.expression, exportName, signatureOnly, seen, inBody);
103
+ return;
104
+ }
105
+ break;
63
106
  }
64
- if (signatureOnly && (node.type === 'FunctionBody' || node.type === 'BlockStatement'))
107
+ const keys = visitorKeys[type];
108
+ if (!keys)
65
109
  return;
66
- if (node.type === 'TSTypeReference' && node.typeName.type === 'Identifier') {
67
- const name = node.typeName.name;
68
- const refs = state.referencedInExport.get(name);
69
- if (refs)
70
- refs.add(exportName);
71
- else
72
- state.referencedInExport.set(name, new Set([exportName]));
73
- }
74
- for (const key in node) {
75
- if (key === 'type' || key === 'parent')
76
- continue;
110
+ const childInBody = inBody || type === 'FunctionBody' || type === 'BlockStatement';
111
+ for (const key of keys) {
77
112
  const val = node[key];
113
+ if (!val)
114
+ continue;
78
115
  if (Array.isArray(val)) {
79
116
  for (const item of val) {
80
- if (item && typeof item === 'object' && item.type)
81
- _collectRefsInType(item, exportName, signatureOnly);
117
+ if (item)
118
+ _collectRefsInType(item, exportName, signatureOnly, seen, childInBody);
82
119
  }
83
120
  }
84
- else if (val && typeof val === 'object' && val.type) {
85
- _collectRefsInType(val, exportName, signatureOnly);
121
+ else {
122
+ _collectRefsInType(val, exportName, signatureOnly, seen, childInBody);
86
123
  }
87
124
  }
88
125
  };
@@ -167,12 +204,15 @@ const coreVisitorObject = {
167
204
  state.nsRanges.push([node.start, node.end]);
168
205
  },
169
206
  ClassDeclaration(node) {
170
- if (node.id?.name)
207
+ if (node.id?.name) {
171
208
  state.localDeclarationTypes.set(node.id.name, SYMBOL_TYPE.CLASS);
209
+ state.localDeclarations.set(node.id.name, node);
210
+ }
172
211
  },
173
212
  FunctionDeclaration(node) {
174
213
  if (node.id?.name) {
175
214
  state.localDeclarationTypes.set(node.id.name, SYMBOL_TYPE.FUNCTION);
215
+ state.localDeclarations.set(node.id.name, node);
176
216
  if (state.scopeDepth > 0)
177
217
  _addShadow(node.id.name);
178
218
  }
@@ -201,8 +241,10 @@ const coreVisitorObject = {
201
241
  }
202
242
  else {
203
243
  for (const decl of node.declarations) {
204
- if (decl.id.type === 'Identifier')
244
+ if (decl.id.type === 'Identifier') {
205
245
  state.localDeclarationTypes.set(decl.id.name, SYMBOL_TYPE.VARIABLE);
246
+ state.localDeclarations.set(decl.id.name, decl);
247
+ }
206
248
  }
207
249
  }
208
250
  },
@@ -651,6 +693,9 @@ export function walkAST(program, sourceText, filePath, ctx) {
651
693
  scopeStarts: [],
652
694
  scopeEnds: [],
653
695
  shadowScopes: new Map(),
696
+ localDeclarations: new Map(),
697
+ pendingCallRefs: [],
698
+ pendingMemberCallRefs: [],
654
699
  addExport: _addExport,
655
700
  getFix: _getFix,
656
701
  getTypeFix: _getTypeFix,
@@ -659,6 +704,35 @@ export function walkAST(program, sourceText, filePath, ctx) {
659
704
  isInNamespace: _isInNamespace,
660
705
  };
661
706
  ctx.visitor.visit(program);
707
+ while (state.pendingCallRefs.length > 0 || state.pendingMemberCallRefs.length > 0) {
708
+ while (state.pendingCallRefs.length > 0) {
709
+ const { name, exportName, seen } = state.pendingCallRefs.pop();
710
+ if (seen.has(name))
711
+ continue;
712
+ const decl = state.localDeclarations.get(name);
713
+ if (!decl)
714
+ continue;
715
+ seen.add(name);
716
+ _collectRefsInType(decl, exportName, true, seen);
717
+ }
718
+ while (state.pendingMemberCallRefs.length > 0) {
719
+ const { objectName, propertyName, exportName, seen } = state.pendingMemberCallRefs.pop();
720
+ const key = `${objectName}.${propertyName}`;
721
+ if (seen.has(key))
722
+ continue;
723
+ const decl = state.localDeclarations.get(objectName);
724
+ if (decl?.type !== 'VariableDeclarator' || decl.init?.type !== 'ObjectExpression')
725
+ continue;
726
+ const prop = decl.init.properties.find(p => p.type === 'Property' && p.key?.type === 'Identifier' && p.key.name === propertyName);
727
+ if (prop?.type !== 'Property')
728
+ continue;
729
+ const fn = prop.value;
730
+ if (fn.type !== 'ArrowFunctionExpression' && fn.type !== 'FunctionExpression')
731
+ continue;
732
+ seen.add(key);
733
+ _collectRefsInType(fn, exportName, true, seen);
734
+ }
735
+ }
662
736
  for (let i = 0; i < state.memberRefsInFile.length; i += 2) {
663
737
  const exp = state.exports.get(state.memberRefsInFile[i]);
664
738
  if (exp) {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.12.0";
1
+ export declare const version = "6.12.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.12.0';
1
+ export const version = '6.12.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.12.0",
3
+ "version": "6.12.1",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",
@@ -80,7 +80,7 @@
80
80
  "fdir": "^6.5.0",
81
81
  "formatly": "^0.3.0",
82
82
  "get-tsconfig": "4.14.0",
83
- "jiti": "^2.6.0",
83
+ "jiti": "^2.7.0",
84
84
  "minimist": "^1.2.8",
85
85
  "oxc-parser": "^0.128.0",
86
86
  "oxc-resolver": "^11.19.1",