knip 5.70.2 → 5.71.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.
Files changed (57) hide show
  1. package/dist/DependencyDeputy.js +2 -2
  2. package/dist/ProjectPrincipal.js +1 -1
  3. package/dist/WorkspaceWorker.js +1 -1
  4. package/dist/binaries/bash-parser.js +14 -1
  5. package/dist/compilers/index.js +1 -1
  6. package/dist/constants.js +1 -0
  7. package/dist/graph/analyze.js +21 -16
  8. package/dist/graph-explorer/constants.d.ts +2 -0
  9. package/dist/graph-explorer/constants.js +2 -0
  10. package/dist/graph-explorer/explorer.d.ts +11 -0
  11. package/dist/graph-explorer/explorer.js +10 -0
  12. package/dist/graph-explorer/operations/build-trace-tree.d.ts +12 -0
  13. package/dist/graph-explorer/operations/build-trace-tree.js +51 -0
  14. package/dist/graph-explorer/operations/has-strictly-ns-references.d.ts +2 -0
  15. package/dist/graph-explorer/operations/has-strictly-ns-references.js +98 -0
  16. package/dist/graph-explorer/operations/is-referenced.d.ts +4 -0
  17. package/dist/graph-explorer/operations/is-referenced.js +89 -0
  18. package/dist/graph-explorer/utils.d.ts +4 -0
  19. package/dist/graph-explorer/utils.js +28 -0
  20. package/dist/graph-explorer/visitors.d.ts +12 -0
  21. package/dist/graph-explorer/visitors.js +30 -0
  22. package/dist/graph-explorer/walk-down.d.ts +4 -0
  23. package/dist/graph-explorer/walk-down.js +119 -0
  24. package/dist/index.js +2 -98
  25. package/dist/plugins/angular/index.js +3 -3
  26. package/dist/plugins/next/index.js +4 -3
  27. package/dist/plugins/next/resolveFromAST.d.ts +1 -0
  28. package/dist/plugins/next/resolveFromAST.js +42 -1
  29. package/dist/plugins/vite/helpers.js +1 -1
  30. package/dist/reporters/codeclimate.js +3 -7
  31. package/dist/reporters/util/util.d.ts +2 -1
  32. package/dist/reporters/util/util.js +1 -0
  33. package/dist/reporters/watch.js +1 -1
  34. package/dist/run.d.ts +25 -0
  35. package/dist/run.js +107 -0
  36. package/dist/types/issues.d.ts +4 -4
  37. package/dist/types/module-graph.d.ts +9 -10
  38. package/dist/typescript/ast-helpers.d.ts +4 -0
  39. package/dist/typescript/ast-helpers.js +48 -0
  40. package/dist/typescript/get-imports-and-exports.js +4 -1
  41. package/dist/typescript/visitors/dynamic-imports/importCall.js +5 -19
  42. package/dist/util/create-options.js +1 -1
  43. package/dist/util/file-entry-cache.js +1 -1
  44. package/dist/util/graph-sequencer.js +1 -1
  45. package/dist/util/module-graph.js +7 -7
  46. package/dist/util/trace.d.ts +3 -13
  47. package/dist/util/trace.js +10 -41
  48. package/dist/util/watch.d.ts +19 -3
  49. package/dist/util/watch.js +28 -17
  50. package/dist/version.d.ts +1 -1
  51. package/dist/version.js +1 -1
  52. package/package.json +1 -1
  53. package/vendor/bash-parser/index.d.ts +6 -1
  54. package/dist/util/has-strictly-ns-references.d.ts +0 -4
  55. package/dist/util/has-strictly-ns-references.js +0 -103
  56. package/dist/util/is-identifier-referenced.d.ts +0 -9
  57. package/dist/util/is-identifier-referenced.js +0 -145
@@ -203,6 +203,7 @@ export const isConsiderReferencedNS = (node) => ts.isPropertyAssignment(node.par
203
203
  ts.isSpreadAssignment(node.parent) ||
204
204
  ts.isArrayLiteralExpression(node.parent) ||
205
205
  ts.isExportAssignment(node.parent) ||
206
+ ts.isConditionalExpression(node.parent) ||
206
207
  (ts.isVariableDeclaration(node.parent) && node.parent.initializer === node) ||
207
208
  ts.isTypeQueryNode(node.parent.parent);
208
209
  export const isInOpaqueExpression = (node) => ts.isAwaitExpression(node.parent)
@@ -263,6 +264,18 @@ export const getImportMap = (sourceFile) => {
263
264
  importMap.set(element.name.text, importPath);
264
265
  }
265
266
  }
267
+ if (ts.isVariableStatement(statement)) {
268
+ for (const declaration of statement.declarationList.declarations) {
269
+ if (declaration.initializer &&
270
+ isRequireCall(declaration.initializer) &&
271
+ ts.isIdentifier(declaration.name) &&
272
+ ts.isStringLiteral(declaration.initializer.arguments[0])) {
273
+ const importName = declaration.name.text;
274
+ const importPath = stripQuotes(declaration.initializer.arguments[0].text);
275
+ importMap.set(importName, importPath);
276
+ }
277
+ }
278
+ }
266
279
  }
267
280
  return importMap;
268
281
  };
@@ -304,6 +317,32 @@ const isMatchAlias = (expression, identifier) => {
304
317
  expression = expression.expression;
305
318
  return expression && ts.isIdentifier(expression) && expression.escapedText === identifier;
306
319
  };
320
+ export function getThenBindings(callExpression) {
321
+ if (!ts.isFunctionLike(callExpression.arguments[0]))
322
+ return;
323
+ const fn = callExpression.arguments[0];
324
+ const param = fn.parameters[0];
325
+ if (!param)
326
+ return;
327
+ if (ts.isIdentifier(param.name)) {
328
+ const paramName = param.name.escapedText;
329
+ const identifiers = [];
330
+ for (const node of findDescendants(fn.body, ts.isPropertyAccessExpression)) {
331
+ if (ts.isIdentifier(node.expression) && node.expression.escapedText === paramName) {
332
+ identifiers.push({ identifier: String(node.name.escapedText), pos: node.name.pos });
333
+ }
334
+ }
335
+ if (identifiers.length > 0)
336
+ return identifiers;
337
+ }
338
+ else if (ts.isObjectBindingPattern(param.name)) {
339
+ return param.name.elements.map(element => {
340
+ const identifier = (element.propertyName ?? element.name).getText();
341
+ const alias = element.propertyName ? element.name.getText() : undefined;
342
+ return { identifier, alias, pos: element.pos };
343
+ });
344
+ }
345
+ }
307
346
  export const getAccessedIdentifiers = (identifier, scope) => {
308
347
  const identifiers = [];
309
348
  function visit(node) {
@@ -328,6 +367,15 @@ export const getAccessedIdentifiers = (identifier, scope) => {
328
367
  }
329
368
  }
330
369
  }
370
+ else if (ts.isCallExpression(node) &&
371
+ ts.isPropertyAccessExpression(node.expression) &&
372
+ isMatchAlias(node.expression.expression, identifier) &&
373
+ node.expression.name.escapedText === 'then') {
374
+ const accessed = getThenBindings(node);
375
+ if (accessed)
376
+ for (const acc of accessed)
377
+ identifiers.push(acc);
378
+ }
331
379
  ts.forEachChild(node, visit);
332
380
  }
333
381
  visit(scope);
@@ -190,9 +190,11 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
190
190
  const addExport = ({ node, symbol, identifier, type, pos, members = [], fix }) => {
191
191
  if (skipExports)
192
192
  return;
193
+ let isReExport = Boolean(node.parent?.parent && ts.isExportDeclaration(node.parent.parent) && node.parent.parent.moduleSpecifier);
193
194
  if (symbol) {
194
195
  const importedSymbolFilePath = importedInternalSymbols.get(symbol);
195
196
  if (importedSymbolFilePath) {
197
+ isReExport = true;
196
198
  const importId = String(symbol.escapedName);
197
199
  const internalImport = internal.get(importedSymbolFilePath);
198
200
  if (internalImport) {
@@ -215,7 +217,7 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
215
217
  const members = [...(item.members ?? []), ...exportMembers];
216
218
  const tags = new Set([...(item.jsDocTags ?? []), ...jsDocTags]);
217
219
  const fixes = fix ? [...(item.fixes ?? []), fix] : item.fixes;
218
- exports.set(identifier, { ...item, members, jsDocTags: tags, fixes });
220
+ exports.set(identifier, { ...item, members, jsDocTags: tags, fixes, isReExport });
219
221
  }
220
222
  else {
221
223
  const { line, character } = node.getSourceFile().getLineAndCharacterOfPosition(pos);
@@ -230,6 +232,7 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
230
232
  col: character + 1,
231
233
  fixes: fix ? [fix] : [],
232
234
  refs: [0, false],
235
+ isReExport,
233
236
  });
234
237
  }
235
238
  if (!jsDocTags.has(ALIAS_TAG)) {
@@ -1,6 +1,6 @@
1
1
  import ts from 'typescript';
2
2
  import { IMPORT_MODIFIERS } from '../../../constants.js';
3
- import { findAncestor, findDescendants, getAccessedIdentifiers, isAccessExpression, isImportCall, isInOpaqueExpression, isTopLevel, stripQuotes, } from '../../ast-helpers.js';
3
+ import { findAncestor, findDescendants, getAccessedIdentifiers, getThenBindings, isAccessExpression, isImportCall, isInOpaqueExpression, isTopLevel, stripQuotes, } from '../../ast-helpers.js';
4
4
  import { importVisitor as visit } from '../index.js';
5
5
  const getSymbol = (node, isTopLevel) => (isTopLevel ? node.symbol : undefined);
6
6
  export default visit(() => true, node => {
@@ -20,24 +20,10 @@ export default visit(() => true, node => {
20
20
  const pos = accessExpression.name.pos;
21
21
  if (identifier === 'then') {
22
22
  const callExpression = node.parent.parent;
23
- if (ts.isCallExpression(callExpression) && ts.isFunctionLike(callExpression.arguments[0])) {
24
- const arg = callExpression.arguments[0].parameters[0];
25
- if (arg && ts.isIdentifier(arg.name)) {
26
- const argName = arg.name.escapedText;
27
- const accessExpressions = findDescendants(callExpression.arguments[0].body, ts.isPropertyAccessExpression).filter(binding => binding.expression.getText() === argName);
28
- if (accessExpressions.length > 0) {
29
- return accessExpressions.map(binding => {
30
- const identifier = String(binding.name.escapedText);
31
- return { identifier, specifier, pos, modifiers };
32
- });
33
- }
34
- }
35
- else if (arg && ts.isObjectBindingPattern(arg.name)) {
36
- return arg.name.elements.map(element => {
37
- const identifier = (element.propertyName ?? element.name).getText();
38
- const alias = element.propertyName ? element.name.getText() : undefined;
39
- return { identifier, alias, specifier, pos: element.pos, modifiers };
40
- });
23
+ if (ts.isCallExpression(callExpression)) {
24
+ const accessed = getThenBindings(callExpression);
25
+ if (accessed && accessed.length > 0) {
26
+ return accessed.map(acc => ({ ...acc, specifier, modifiers }));
41
27
  }
42
28
  }
43
29
  return { identifier: 'default', specifier, pos, modifiers };
@@ -115,7 +115,7 @@ export const createOptions = async (options) => {
115
115
  rules,
116
116
  tags,
117
117
  traceExport: parsedCLIArgs['trace-export'],
118
- traceFile: parsedCLIArgs['trace-file'],
118
+ traceFile: parsedCLIArgs['trace-file'] ? toAbsolute(parsedCLIArgs['trace-file'], cwd) : undefined,
119
119
  tsConfigFile: parsedCLIArgs.tsConfig,
120
120
  workspace: options.workspace ?? parsedCLIArgs.workspace,
121
121
  workspaces,
@@ -86,7 +86,7 @@ export class FileEntryCache {
86
86
  }
87
87
  reconcile() {
88
88
  this.removeNotFoundFiles();
89
- for (const [entryName, cacheEntry] of this.normalizedEntries.entries()) {
89
+ for (const [entryName, cacheEntry] of this.normalizedEntries) {
90
90
  try {
91
91
  const meta = this._getMetaForFileUsingMtimeAndSize(cacheEntry);
92
92
  this.cache.set(entryName, meta);
@@ -6,7 +6,7 @@ export function graphSequencer(graph, includedNodes = [...graph.keys()]) {
6
6
  const nodes = new Set(includedNodes);
7
7
  const visited = new Set();
8
8
  const outDegree = new Map();
9
- for (const [from, edges] of graph.entries()) {
9
+ for (const [from, edges] of graph) {
10
10
  outDegree.set(from, 0);
11
11
  for (const to of edges) {
12
12
  if (nodes.has(from) && nodes.has(to)) {
@@ -2,21 +2,21 @@ export const getOrCreateFileNode = (graph, filePath) => graph.get(filePath) ?? c
2
2
  const updateImportMaps = (fromImportMaps, toImportMaps) => {
3
3
  for (const id of fromImportMaps.refs)
4
4
  toImportMaps.refs.add(id);
5
- for (const [id, v] of fromImportMaps.imported.entries())
5
+ for (const [id, v] of fromImportMaps.imported)
6
6
  addValues(toImportMaps.imported, id, v);
7
- for (const [id, v] of fromImportMaps.importedAs.entries())
7
+ for (const [id, v] of fromImportMaps.importedAs)
8
8
  addNsValues(toImportMaps.importedAs, id, v);
9
- for (const [id, v] of fromImportMaps.importedNs.entries())
9
+ for (const [id, v] of fromImportMaps.importedNs)
10
10
  addValues(toImportMaps.importedNs, id, v);
11
- for (const [id, v] of fromImportMaps.reExported.entries())
11
+ for (const [id, v] of fromImportMaps.reExported)
12
12
  addValues(toImportMaps.reExported, id, v);
13
- for (const [id, v] of fromImportMaps.reExportedAs.entries())
13
+ for (const [id, v] of fromImportMaps.reExportedAs)
14
14
  addNsValues(toImportMaps.reExportedAs, id, v);
15
- for (const [id, v] of fromImportMaps.reExportedNs.entries())
15
+ for (const [id, v] of fromImportMaps.reExportedNs)
16
16
  addValues(toImportMaps.reExportedNs, id, v);
17
17
  };
18
18
  export const updateImportMap = (file, importMap, graph) => {
19
- for (const [importedFilePath, fileImportMaps] of importMap.entries()) {
19
+ for (const [importedFilePath, fileImportMaps] of importMap) {
20
20
  const importMaps = file.imports.internal.get(importedFilePath);
21
21
  if (!importMaps)
22
22
  file.imports.internal.set(importedFilePath, fileImportMaps);
@@ -1,20 +1,10 @@
1
- import type { ModuleGraph } from '../types/module-graph.js';
2
1
  import type { MainOptions } from './create-options.js';
3
- type CreateNodeOpts = {
4
- identifier?: string;
5
- hasRef?: boolean;
6
- isEntry?: boolean;
7
- };
8
- type Create = (filePath: string, options?: CreateNodeOpts) => TraceNode;
9
- export type TraceNode = {
2
+ type ExplorerTraceNode = {
10
3
  filePath: string;
11
4
  identifier?: string;
12
5
  hasRef: boolean;
13
6
  isEntry: boolean;
14
- children: Set<TraceNode>;
7
+ children: ExplorerTraceNode[];
15
8
  };
16
- export declare const printTrace: (node: TraceNode, filePath: string, options: MainOptions, identifier?: string) => void;
17
- export declare const createNode: Create;
18
- export declare const addNodes: (node: TraceNode, id: string, importedSymbols: ModuleGraph, filePaths?: Set<string>) => void;
19
- export declare const createAndPrintTrace: (filePath: string, options: MainOptions, opts?: CreateNodeOpts) => void;
9
+ export declare const printTraceNode: (node: ExplorerTraceNode, options: MainOptions) => void;
20
10
  export {};
@@ -1,5 +1,5 @@
1
1
  import picocolors from 'picocolors';
2
- import { toAbsolute, toRelative } from './path.js';
2
+ import { toRelative } from './path.js';
3
3
  const IS_ENTRY = ' ◯';
4
4
  const HAS_REF = ' ✓';
5
5
  const HAS_NO_REF = ' x';
@@ -9,62 +9,31 @@ const getPadding = (level, levels) => {
9
9
  padding += levels.has(i) ? `${picocolors.dim('│')} ` : ' ';
10
10
  return padding;
11
11
  };
12
- const renderTrace = (node, options, level = 0, levels = new Set()) => {
12
+ const renderExplorerTrace = (node, options, level = 0, levels = new Set()) => {
13
13
  let index = 0;
14
- const size = node.children.size;
14
+ const size = node.children.length;
15
15
  const padding = getPadding(level, levels);
16
16
  for (const child of node.children) {
17
17
  const isLast = ++index === size;
18
18
  const hasRef = child.hasRef === true;
19
19
  const rel = toRelative(child.filePath, options.cwd);
20
20
  const file = hasRef ? rel : picocolors.dim(rel);
21
- const suffix = (hasRef ? HAS_REF : '') + (child.isEntry ? IS_ENTRY : '');
21
+ const suffix = hasRef ? HAS_REF : '';
22
22
  const text = `${padding}${picocolors.dim(isLast ? '└─' : '├─')} ${file}${suffix}`;
23
23
  console.log(text);
24
- if (child.children.size > 0) {
24
+ if (child.children.length > 0) {
25
25
  if (!isLast)
26
26
  levels.add(level);
27
27
  if (isLast)
28
28
  levels.delete(level);
29
- renderTrace(child, options, level + 1, levels);
29
+ renderExplorerTrace(child, options, level + 1, levels);
30
30
  }
31
31
  }
32
32
  };
33
- export const printTrace = (node, filePath, options, identifier) => {
34
- if (!options.isTrace)
35
- return;
36
- if (options.traceExport && identifier !== options.traceExport)
37
- return;
38
- if (options.traceFile && filePath !== toAbsolute(options.traceFile, options.cwd))
39
- return;
40
- const suffix = (node.isEntry ? IS_ENTRY : '') + (node.children.size === 0 ? HAS_NO_REF : '');
41
- const header = `${toRelative(filePath, options.cwd)}${identifier ? `:${identifier}` : ''}${suffix}`;
33
+ export const printTraceNode = (node, options) => {
34
+ const suffix = (node.isEntry ? IS_ENTRY : '') + (node.children.length === 0 ? HAS_NO_REF : '');
35
+ const header = `${toRelative(node.filePath, options.cwd)}${node.identifier ? `:${node.identifier}` : ''}${suffix}`;
42
36
  console.log(header);
43
- renderTrace(node, options);
37
+ renderExplorerTrace(node, options);
44
38
  console.log();
45
39
  };
46
- export const createNode = (filePath, { hasRef = false, isEntry = false, identifier } = {}) => ({
47
- filePath,
48
- identifier,
49
- hasRef,
50
- isEntry,
51
- children: new Set(),
52
- });
53
- const addNode = (parent, filePath, { hasRef = false, isEntry = false }) => {
54
- const node = createNode(filePath, { hasRef, isEntry });
55
- parent.children.add(node);
56
- return node;
57
- };
58
- export const addNodes = (node, id, importedSymbols, filePaths) => {
59
- if (!filePaths)
60
- return;
61
- for (const filePath of filePaths) {
62
- addNode(node, filePath, { hasRef: Boolean(importedSymbols.get(filePath)?.traceRefs?.has(id)) });
63
- }
64
- };
65
- export const createAndPrintTrace = (filePath, options, opts = {}) => {
66
- if (!options.isTrace)
67
- return;
68
- const traceNode = createNode(filePath, opts);
69
- printTrace(traceNode, filePath, options, opts.identifier);
70
- };
@@ -6,10 +6,16 @@ import type { ProjectPrincipal } from '../ProjectPrincipal.js';
6
6
  import type { Issues } from '../types/issues.js';
7
7
  import type { ModuleGraph } from '../types/module-graph.js';
8
8
  import type { MainOptions } from './create-options.js';
9
- export type OnUpdate = (options: {
9
+ export type OnFileChange = (options: {
10
10
  issues: Issues;
11
11
  duration?: number;
12
+ mem?: number;
12
13
  }) => void;
14
+ type WatchChange = {
15
+ type: 'added' | 'deleted' | 'modified';
16
+ filePath: string;
17
+ };
18
+ export type WatchHandler = Awaited<ReturnType<typeof getWatchHandler>>;
13
19
  type Watch = {
14
20
  analyzedFiles: Set<string>;
15
21
  analyzeSourceFile: (filePath: string, principal: ProjectPrincipal) => void;
@@ -19,8 +25,18 @@ type Watch = {
19
25
  factory: PrincipalFactory;
20
26
  graph: ModuleGraph;
21
27
  isIgnored: (path: string) => boolean;
22
- onUpdate: OnUpdate;
28
+ onFileChange?: OnFileChange;
23
29
  unreferencedFiles: Set<string>;
30
+ entryPaths: Set<string>;
24
31
  };
25
- export declare const getWatchHandler: (options: MainOptions, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, factory, graph, isIgnored, onUpdate, unreferencedFiles, }: Watch) => Promise<WatchListener<string | Buffer<ArrayBufferLike>>>;
32
+ export declare const getWatchHandler: (options: MainOptions, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, factory, graph, isIgnored, onFileChange, unreferencedFiles, entryPaths, }: Watch) => Promise<{
33
+ listener: WatchListener<string | Buffer<ArrayBufferLike>>;
34
+ handleFileChanges: (changes: WatchChange[]) => Promise<{
35
+ duration: number;
36
+ mem: number;
37
+ }>;
38
+ getEntryPaths: () => Set<string>;
39
+ getGraph: () => ModuleGraph;
40
+ getIssues: () => Issues;
41
+ }>;
26
42
  export {};
@@ -2,18 +2,23 @@ import { debugLog } from './debug.js';
2
2
  import { isFile } from './fs.js';
3
3
  import { updateImportMap } from './module-graph.js';
4
4
  import { join, toAbsolute, toRelative } from './path.js';
5
- export const getWatchHandler = async (options, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, factory, graph, isIgnored, onUpdate, unreferencedFiles, }) => {
5
+ const createUpdate = (options) => {
6
+ const duration = performance.now() - options.startTime;
7
+ const mem = process.memoryUsage().heapUsed;
8
+ return { duration, mem };
9
+ };
10
+ export const getWatchHandler = async (options, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, factory, graph, isIgnored, onFileChange, unreferencedFiles, entryPaths, }) => {
6
11
  const getIssues = () => collector.getIssues().issues;
7
- const processBatch = async (changes) => {
12
+ const handleFileChanges = async (changes) => {
8
13
  const startTime = performance.now();
9
14
  const added = new Set();
10
15
  const deleted = new Set();
11
16
  const modified = new Set();
12
- for (const [type, _path] of changes) {
13
- const filePath = toAbsolute(_path, options.cwd);
14
- const relativePath = toRelative(_path, options.cwd);
17
+ for (const change of changes) {
18
+ const filePath = toAbsolute(change.filePath, options.cwd);
19
+ const relativePath = toRelative(change.filePath, options.cwd);
15
20
  if (isIgnored(filePath)) {
16
- debugLog('*', `ignoring ${type} ${relativePath}`);
21
+ debugLog('*', `ignoring ${change.type} ${relativePath}`);
17
22
  continue;
18
23
  }
19
24
  const workspace = chief.findWorkspaceByFilePath(filePath);
@@ -22,7 +27,7 @@ export const getWatchHandler = async (options, { analyzedFiles, analyzeSourceFil
22
27
  const principal = factory.getPrincipalByPackageName(workspace.pkgName);
23
28
  if (!principal)
24
29
  continue;
25
- switch (type) {
30
+ switch (change.type) {
26
31
  case 'added':
27
32
  added.add(filePath);
28
33
  principal.addProjectPath(filePath);
@@ -35,7 +40,7 @@ export const getWatchHandler = async (options, { analyzedFiles, analyzeSourceFil
35
40
  principal.removeProjectPath(filePath);
36
41
  debugLog(workspace.name, `Watcher: - ${relativePath}`);
37
42
  break;
38
- case 'modified':
43
+ default:
39
44
  modified.add(filePath);
40
45
  debugLog(workspace.name, `Watcher: ± ${relativePath}`);
41
46
  break;
@@ -43,7 +48,7 @@ export const getWatchHandler = async (options, { analyzedFiles, analyzeSourceFil
43
48
  principal.invalidateFile(filePath);
44
49
  }
45
50
  if (added.size === 0 && deleted.size === 0 && modified.size === 0)
46
- return;
51
+ return createUpdate({ startTime });
47
52
  unreferencedFiles.clear();
48
53
  const cachedUnusedFiles = collector.purge();
49
54
  for (const filePath of added)
@@ -110,15 +115,21 @@ export const getWatchHandler = async (options, { analyzedFiles, analyzeSourceFil
110
115
  collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });
111
116
  for (const issue of collector.getRetainedIssues())
112
117
  collector.addIssue(issue);
113
- onUpdate({ issues: getIssues(), duration: performance.now() - startTime });
118
+ const update = createUpdate({ startTime });
119
+ if (onFileChange)
120
+ onFileChange(Object.assign({ issues: getIssues() }, update));
121
+ return update;
114
122
  };
115
- const listener = (eventType, filename) => {
116
- debugLog('*', `(raw) ${eventType} ${filename}`);
117
- if (typeof filename === 'string') {
118
- const event = eventType === 'rename' ? (isFile(join(options.cwd, filename)) ? 'added' : 'deleted') : 'modified';
119
- processBatch([[event, filename]]);
123
+ const listener = (eventType, filePath) => {
124
+ debugLog('*', `(raw) ${eventType} ${filePath}`);
125
+ if (typeof filePath === 'string') {
126
+ const type = eventType === 'rename' ? (isFile(join(options.cwd, filePath)) ? 'added' : 'deleted') : 'modified';
127
+ handleFileChanges([{ type, filePath }]);
120
128
  }
121
129
  };
122
- onUpdate({ issues: getIssues() });
123
- return listener;
130
+ if (onFileChange)
131
+ onFileChange({ issues: getIssues() });
132
+ const getEntryPaths = () => entryPaths;
133
+ const getGraph = () => graph;
134
+ return { listener, handleFileChanges, getEntryPaths, getGraph, getIssues };
124
135
  };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.70.2";
1
+ export declare const version = "5.71.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.70.2';
1
+ export const version = '5.71.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.70.2",
3
+ "version": "5.71.0",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {
@@ -43,6 +43,11 @@ type Function_ = {
43
43
  body: CompoundList;
44
44
  };
45
45
 
46
+ type Subshell = {
47
+ type: 'Subshell';
48
+ list: CompoundList;
49
+ };
50
+
46
51
  export type ExpansionNode = {
47
52
  expansion: Expansion[];
48
53
  };
@@ -62,7 +67,7 @@ export type Assignment = {
62
67
  text: string;
63
68
  };
64
69
 
65
- export type Node = CompoundList | Command | LogicalExpression | If | For | Function_ | Pipeline;
70
+ export type Node = CompoundList | Command | LogicalExpression | If | For | Function_ | Pipeline | Subshell;
66
71
 
67
72
  export type AST = {
68
73
  type: 'Script';
@@ -1,4 +0,0 @@
1
- import type { ImportMaps, ModuleGraph } from '../types/module-graph.js';
2
- export declare const hasStrictlyEnumReferences: (importsForExport: ImportMaps | undefined, id: string) => boolean;
3
- export declare const hasStrictlyNsReferences: (graph: ModuleGraph, importsForExport: ImportMaps | undefined, id: string) => [boolean, string?];
4
- export declare const getType: (hasOnlyNsReference: boolean, isType: boolean) => "exports" | "nsExports" | "types" | "nsTypes";
@@ -1,103 +0,0 @@
1
- import { IMPORT_STAR } from '../constants.js';
2
- export const hasStrictlyEnumReferences = (importsForExport, id) => {
3
- if (!importsForExport || !importsForExport.refs.has(id))
4
- return false;
5
- for (const ref of importsForExport.refs)
6
- if (ref.startsWith(`${id}.`))
7
- return false;
8
- return true;
9
- };
10
- export const hasStrictlyNsReferences = (graph, importsForExport, id) => {
11
- if (!importsForExport)
12
- return [false];
13
- let namespace;
14
- for (const ns of importsForExport.importedNs.keys()) {
15
- const hasNs = importsForExport.refs.has(ns);
16
- if (!hasNs)
17
- return [false, ns];
18
- for (const id of importsForExport.refs)
19
- if (id.startsWith(`${ns}.`))
20
- return [false, ns];
21
- const byFilePaths = importsForExport.reExportedNs.get(ns);
22
- if (byFilePaths) {
23
- for (const filePath of byFilePaths) {
24
- const file = graph.get(filePath);
25
- if (file?.imported) {
26
- const hasStrictlyNsRefs = hasStrictlyNsReferences(graph, file.imported, id);
27
- if (hasStrictlyNsRefs[0] === false)
28
- return hasStrictlyNsRefs;
29
- }
30
- }
31
- }
32
- const reExportedAs = importsForExport.reExportedAs.get(ns);
33
- if (reExportedAs) {
34
- for (const byFilePaths of reExportedAs.values()) {
35
- for (const filePath of byFilePaths) {
36
- const file = graph.get(filePath);
37
- if (file?.imported) {
38
- const hasStrictlyNsRefs = hasStrictlyNsReferences(graph, file.imported, id);
39
- if (hasStrictlyNsRefs[0] === false)
40
- return hasStrictlyNsRefs;
41
- }
42
- }
43
- }
44
- }
45
- namespace = ns;
46
- }
47
- const byFilePaths = importsForExport.reExported.get(id);
48
- if (byFilePaths) {
49
- for (const filePath of byFilePaths) {
50
- const file = graph.get(filePath);
51
- if (file?.imported) {
52
- const hasStrictlyNsRefs = hasStrictlyNsReferences(graph, file.imported, id);
53
- if (hasStrictlyNsRefs[0] === false)
54
- return hasStrictlyNsRefs;
55
- namespace = hasStrictlyNsRefs[1];
56
- }
57
- }
58
- }
59
- {
60
- const byFilePaths = importsForExport.reExported.get(IMPORT_STAR);
61
- if (byFilePaths) {
62
- for (const filePath of byFilePaths) {
63
- const file = graph.get(filePath);
64
- if (file?.imported) {
65
- const hasStrictlyNsRefs = hasStrictlyNsReferences(graph, file.imported, id);
66
- if (hasStrictlyNsRefs[0] === false)
67
- return hasStrictlyNsRefs;
68
- namespace = hasStrictlyNsRefs[1];
69
- }
70
- }
71
- }
72
- }
73
- const [identifier, ...rest] = id.split('.');
74
- const reExportedAs = importsForExport.reExportedAs.get(identifier);
75
- if (reExportedAs) {
76
- for (const [alias, filePaths] of reExportedAs.entries()) {
77
- for (const filePath of filePaths) {
78
- const file = graph.get(filePath);
79
- if (file?.imported) {
80
- const hasStrictlyNsRefs = hasStrictlyNsReferences(graph, file.imported, [alias, ...rest].join('.'));
81
- if (hasStrictlyNsRefs[0] === false)
82
- return hasStrictlyNsRefs;
83
- namespace = hasStrictlyNsRefs[1];
84
- }
85
- }
86
- }
87
- }
88
- for (const [ns, filePaths] of importsForExport.reExportedNs.entries()) {
89
- for (const filePath of filePaths) {
90
- const file = graph.get(filePath);
91
- if (file?.imported) {
92
- const hasStrictlyNsRefs = hasStrictlyNsReferences(graph, file.imported, `${ns}.${id}`);
93
- if (hasStrictlyNsRefs[0] === false)
94
- return hasStrictlyNsRefs;
95
- namespace = hasStrictlyNsRefs[1];
96
- }
97
- }
98
- }
99
- if (namespace)
100
- return [true, namespace];
101
- return [false];
102
- };
103
- export const getType = (hasOnlyNsReference, isType) => hasOnlyNsReference ? (isType ? 'nsTypes' : 'nsExports') : isType ? 'types' : 'exports';
@@ -1,9 +0,0 @@
1
- import type { ModuleGraph } from '../types/module-graph.js';
2
- import { type TraceNode } from './trace.js';
3
- type Result = {
4
- isReferenced: boolean;
5
- reExportingEntryFile: undefined | string;
6
- traceNode: TraceNode;
7
- };
8
- export declare const getIsIdentifierReferencedHandler: (graph: ModuleGraph, entryPaths: Set<string>, isTrace: boolean) => (filePath: string, id: string, isIncludeEntryExports?: boolean, traceNode?: TraceNode, seen?: Set<string>) => Result;
9
- export {};