knip 6.6.2 → 6.6.3

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/dist/cli.js CHANGED
@@ -74,7 +74,8 @@ const main = async () => {
74
74
  }
75
75
  if ((!args['no-exit-code'] && totalErrorCount > Number(args['max-issues'] ?? 0)) ||
76
76
  (!options.isDisableConfigHints && options.isTreatConfigHintsAsErrors && configurationHints.length > 0)) {
77
- process.exit(1);
77
+ process.exitCode = 1;
78
+ return;
78
79
  }
79
80
  }
80
81
  catch (error) {
@@ -92,10 +93,11 @@ const main = async () => {
92
93
  }
93
94
  if (isConfigurationError(knownErrors[0]))
94
95
  console.log('\nRun `knip --help` or visit https://knip.dev for help');
95
- process.exit(2);
96
+ process.exitCode = 2;
97
+ return;
96
98
  }
97
99
  throw error;
98
100
  }
99
- process.exit(0);
101
+ process.exitCode = 0;
100
102
  };
101
103
  await main();
@@ -1,6 +1,7 @@
1
1
  import { createGraphExplorer } from '../graph-explorer/explorer.js';
2
2
  import { getIssueType, hasStrictlyEnumReferences } from '../graph-explorer/utils.js';
3
3
  import traceReporter from '../reporters/trace.js';
4
+ import { shouldCountRefs } from '../typescript/visitors/helpers.js';
4
5
  import { getPackageNameFromModuleSpecifier } from '../util/modules.js';
5
6
  import { perfObserver } from '../util/Performance.js';
6
7
  import { findMatch } from '../util/regex.js';
@@ -9,6 +10,7 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
9
10
  const shouldIgnore = getShouldIgnoreHandler(options.isProduction);
10
11
  const shouldIgnoreTags = getShouldIgnoreTagHandler(options.tags);
11
12
  const explorer = createGraphExplorer(graph, entryPaths);
13
+ const ignoreExportsUsedInFile = chief.config.ignoreExportsUsedInFile;
12
14
  const isReferencedInUsedExport = (exportedItem, filePath, includeEntryExports, visited) => {
13
15
  if (!exportedItem.referencedIn)
14
16
  return false;
@@ -16,13 +18,15 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
16
18
  if (!file)
17
19
  return false;
18
20
  for (const containingExport of exportedItem.referencedIn) {
19
- if (explorer.isReferenced(filePath, containingExport, { includeEntryExports })[0])
20
- return true;
21
21
  const inExport = file.exports.get(containingExport);
22
22
  if (!inExport)
23
23
  continue;
24
- if (inExport.hasRefsInFile && (inExport.type === 'type' || inExport.type === 'interface'))
25
- return true;
24
+ if (shouldCountRefs(ignoreExportsUsedInFile, inExport.type)) {
25
+ if (inExport.hasRefsInFile)
26
+ return true;
27
+ if (explorer.isReferenced(filePath, containingExport, { includeEntryExports })[0])
28
+ return true;
29
+ }
26
30
  if (inExport.referencedIn) {
27
31
  const v = visited ?? new Set();
28
32
  if (!v.has(containingExport)) {
@@ -25,11 +25,11 @@ export interface NuxtConfig {
25
25
  css?: string[];
26
26
  alias?: Record<string, string>;
27
27
  }
28
- export interface TemplateExpressionNode {
28
+ interface TemplateExpressionNode {
29
29
  content: string;
30
30
  isStatic: boolean;
31
31
  }
32
- export interface TemplateAstProp {
32
+ interface TemplateAstProp {
33
33
  type: number;
34
34
  exp?: TemplateExpressionNode;
35
35
  arg?: TemplateExpressionNode;
@@ -41,7 +41,7 @@ export interface TemplateAstNode {
41
41
  content?: TemplateExpressionNode;
42
42
  children?: TemplateAstNode[];
43
43
  }
44
- export interface Descriptor {
44
+ interface Descriptor {
45
45
  script: {
46
46
  content: string;
47
47
  } | null;
@@ -58,3 +58,4 @@ export type VueSfc = {
58
58
  descriptor: Descriptor;
59
59
  };
60
60
  };
61
+ export {};
@@ -1,10 +1,11 @@
1
- export type RaycastManifestCommand = {
1
+ type RaycastManifestCommand = {
2
2
  name?: unknown;
3
3
  };
4
- export type RaycastManifestTool = {
4
+ type RaycastManifestTool = {
5
5
  name?: unknown;
6
6
  };
7
7
  export type RaycastManifest = {
8
8
  commands?: RaycastManifestCommand[];
9
9
  tools?: RaycastManifestTool[];
10
10
  };
11
+ export {};
@@ -7,7 +7,7 @@ export default ({ graph, explorer, options, workspaceFilePathFilter }) => {
7
7
  if (options.traceDependency) {
8
8
  const pattern = toRegexOrString(options.traceDependency);
9
9
  const toRel = (path) => toRelative(path, options.cwd);
10
- const table = new Table({ truncateStart: ['filePath'] });
10
+ const table = new Table({ truncate: { filePath: 'start' } });
11
11
  const seen = new Set();
12
12
  for (const [packageName, { imports }] of explorer.getDependencyUsage(pattern)) {
13
13
  const filtered = imports.filter(i => workspaceFilePathFilter(i.filePath));
@@ -17,7 +17,7 @@ const getIdentifier = (hint) => {
17
17
  return hint.identifier.toString();
18
18
  };
19
19
  const getTableForHints = (hints) => {
20
- const table = new Table({ truncateStart: ['identifier', 'workspace', 'filePath'] });
20
+ const table = new Table({ truncate: { identifier: 'start', workspace: 'start', filePath: 'start' } });
21
21
  for (const hint of hints) {
22
22
  table.row();
23
23
  table.cell('identifier', getIdentifier(hint));
@@ -41,7 +41,7 @@ const highlightSymbol = (issue) => (_) => {
41
41
  return symbol;
42
42
  };
43
43
  export const getTableForType = (issues, cwd, options = { isUseColors: true }) => {
44
- const table = new Table({ truncateStart: ['filePath'], noTruncate: ['symbolType'] });
44
+ const table = new Table({ truncate: { filePath: 'start', symbolType: 'none' } });
45
45
  for (const issue of issues.sort(sortByPos)) {
46
46
  table.row();
47
47
  const print = options.isUseColors && (issue.isFixed || issue.severity === 'warn') ? dim : plain;
@@ -100,7 +100,7 @@ export type SourceMap = {
100
100
  srcDir: string;
101
101
  outDir: string;
102
102
  };
103
- export interface ResolveSourceMapOptions {
103
+ interface ResolveSourceMapOptions {
104
104
  cwd: string;
105
105
  manifest: Manifest;
106
106
  dependencies: Set<string>;
@@ -109,7 +109,7 @@ export interface ResolveSourceMapOptions {
109
109
  }
110
110
  export type ResolveSourceMap = (options: ResolveSourceMapOptions) => Promise<SourceMap[]> | SourceMap[];
111
111
  export type HandleInput = (input: Input) => string | undefined;
112
- export type RegisterCompilerInput = {
112
+ type RegisterCompilerInput = {
113
113
  extension: string;
114
114
  compiler: CompilerSync;
115
115
  };
@@ -117,7 +117,7 @@ export type RegisterCompiler = (input: RegisterCompilerInput) => void;
117
117
  export type ResolveFromAST = (program: Program, options: PluginOptions & {
118
118
  readFile: (filePath: string) => string;
119
119
  }) => Input[];
120
- export type RegisterCompilersOptions = {
120
+ type RegisterCompilersOptions = {
121
121
  cwd: string;
122
122
  hasDependency: HasDependency;
123
123
  registerCompiler: RegisterCompiler;
@@ -52,7 +52,7 @@ export interface ExportMember extends Position {
52
52
  readonly flags: number;
53
53
  hasRefsInFile: boolean;
54
54
  }
55
- export type ExportMap = Map<Identifier, Export>;
55
+ type ExportMap = Map<Identifier, Export>;
56
56
  export type Imports = Set<Import>;
57
57
  export type FileNode = {
58
58
  imports: {
@@ -63,7 +63,7 @@ export interface WalkState extends WalkContext {
63
63
  addExport: (identifier: string, type: SymbolType, pos: number, members: ExportMember[], fix: Fix, isReExport: boolean, jsDocTags: Set<string>) => void;
64
64
  getFix: (start: number, end: number, flags?: number) => Fix;
65
65
  getTypeFix: (start: number, end: number) => Fix;
66
- collectRefsInType: (node: any, exportName: string, signatureOnly: boolean, inMember?: boolean) => void;
66
+ collectRefsInType: (node: any, exportName: string, signatureOnly: boolean) => void;
67
67
  addRefInExport: (name: string, exportName: string) => void;
68
68
  isInNamespace: (node: Span) => boolean;
69
69
  }
@@ -47,26 +47,17 @@ const _addExport = (identifier, type, pos, members, fix, isReExport, jsDocTags)
47
47
  });
48
48
  }
49
49
  };
50
- const MEMBER_CONTAINERS = new Set([
51
- 'TSPropertySignature',
52
- 'TSMethodSignature',
53
- 'TSIndexSignature',
54
- 'TSCallSignatureDeclaration',
55
- 'TSConstructSignatureDeclaration',
56
- ]);
57
- const _collectRefsInType = (node, exportName, signatureOnly, inMember = false) => {
50
+ const _collectRefsInType = (node, exportName, signatureOnly) => {
58
51
  if (!node || typeof node !== 'object')
59
52
  return;
60
53
  if (node.type === 'TSTypeQuery') {
61
- if (inMember) {
62
- const name = node.exprName.type === 'Identifier' ? node.exprName.name : undefined;
63
- if (name) {
64
- const refs = state.referencedInExport.get(name);
65
- if (refs)
66
- refs.add(exportName);
67
- else
68
- state.referencedInExport.set(name, new Set([exportName]));
69
- }
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]));
70
61
  }
71
62
  return;
72
63
  }
@@ -80,7 +71,6 @@ const _collectRefsInType = (node, exportName, signatureOnly, inMember = false) =
80
71
  else
81
72
  state.referencedInExport.set(name, new Set([exportName]));
82
73
  }
83
- const nextInMember = inMember || MEMBER_CONTAINERS.has(node.type);
84
74
  for (const key in node) {
85
75
  if (key === 'type' || key === 'parent')
86
76
  continue;
@@ -88,11 +78,11 @@ const _collectRefsInType = (node, exportName, signatureOnly, inMember = false) =
88
78
  if (Array.isArray(val)) {
89
79
  for (const item of val) {
90
80
  if (item && typeof item === 'object' && item.type)
91
- _collectRefsInType(item, exportName, signatureOnly, nextInMember);
81
+ _collectRefsInType(item, exportName, signatureOnly);
92
82
  }
93
83
  }
94
84
  else if (val && typeof val === 'object' && val.type) {
95
- _collectRefsInType(val, exportName, signatureOnly, nextInMember);
85
+ _collectRefsInType(val, exportName, signatureOnly);
96
86
  }
97
87
  }
98
88
  };
@@ -111,7 +111,7 @@ class Performance {
111
111
  table.cell('sum', stats.sum, twoFixed);
112
112
  table.cell('%', (stats.sum / totalDuration) * 100, v => (typeof v === 'number' ? `${v.toFixed(0)}%` : ''));
113
113
  }
114
- table.sort('sum|desc');
114
+ table.sort('sum', 'desc');
115
115
  return table.toString();
116
116
  }
117
117
  addMemoryMark(label) {
@@ -1,20 +1,22 @@
1
1
  type Value = string | number | undefined | false | null;
2
+ type TruncateMode = 'start' | 'end' | 'none';
3
+ type SortOrder = 'asc' | 'desc';
2
4
  export declare class Table {
3
5
  private columns;
4
6
  private rows;
5
7
  private header;
6
8
  private maxWidth;
7
- private truncateStart;
8
- private noTruncate;
9
+ private truncateModes;
9
10
  constructor(options?: {
10
11
  maxWidth?: number;
11
12
  header?: boolean;
12
- truncateStart?: string[];
13
- noTruncate?: string[];
13
+ truncate?: Record<string, TruncateMode>;
14
14
  });
15
15
  row(): this;
16
16
  cell(column: string, value: Value, formatter?: (value: Value) => string): this;
17
- sort(column: string): this;
17
+ sort(column: string, order?: SortOrder): this;
18
+ private modeFor;
19
+ private distributeWidths;
18
20
  toCells(): string[][];
19
21
  toRows(): string[];
20
22
  toString(): string;
@@ -1,20 +1,20 @@
1
1
  import { stripVTControlCharacters } from 'node:util';
2
2
  import { pad, truncate, truncateStart } from './string.js';
3
- const DEFAULT_MAX_WIDTH = process.stdout.columns || 120;
4
3
  const MIN_TRUNCATED_WIDTH = 4;
5
4
  const COLUMN_SEPARATOR = ' ';
5
+ const visibleLength = (text) => stripVTControlCharacters(text).length;
6
+ const isPrintable = (value) => typeof value === 'string' || typeof value === 'number';
7
+ const toDisplay = (value) => (isPrintable(value) ? String(value) : '');
6
8
  export class Table {
7
9
  columns = [];
8
10
  rows = [];
9
11
  header;
10
12
  maxWidth;
11
- truncateStart = [];
12
- noTruncate = [];
13
+ truncateModes;
13
14
  constructor(options) {
14
15
  this.header = options?.header ?? false;
15
- this.maxWidth = options?.maxWidth || DEFAULT_MAX_WIDTH;
16
- this.truncateStart = options?.truncateStart || [];
17
- this.noTruncate = options?.noTruncate || [];
16
+ this.maxWidth = options?.maxWidth || process.stdout.columns || 120;
17
+ this.truncateModes = options?.truncate ?? {};
18
18
  }
19
19
  row() {
20
20
  this.rows.push({});
@@ -25,67 +25,103 @@ export class Table {
25
25
  this.columns.push(column);
26
26
  const row = this.rows[this.rows.length - 1];
27
27
  const align = typeof value === 'number' ? 'right' : 'left';
28
- const formatted = formatter ? formatter(value) : undefined;
29
- row[column] = { value, formatted, align };
28
+ const formatted = formatter?.(value);
29
+ const display = formatted ?? toDisplay(value);
30
+ row[column] = { value, formatted, align, width: visibleLength(display) };
30
31
  return this;
31
32
  }
32
- sort(column) {
33
+ sort(column, order = 'asc') {
34
+ const dir = order === 'desc' ? -1 : 1;
33
35
  this.rows.sort((a, b) => {
34
- const [columnName, order] = column.split('|');
35
- const vA = a[columnName].value;
36
- const vB = b[columnName].value;
36
+ const vA = a[column]?.value;
37
+ const vB = b[column]?.value;
37
38
  if (typeof vA === 'string' && typeof vB === 'string')
38
- return (order === 'desc' ? -1 : 1) * vA.localeCompare(vB);
39
+ return dir * vA.localeCompare(vB);
39
40
  if (typeof vA === 'number' && typeof vB === 'number')
40
- return order === 'desc' ? vB - vA : vA - vB;
41
- return !vA ? 1 : !vB ? -1 : 0;
41
+ return dir * (vA - vB);
42
+ return !isPrintable(vA) ? 1 : !isPrintable(vB) ? -1 : 0;
42
43
  });
43
44
  return this;
44
45
  }
46
+ modeFor(column) {
47
+ return this.truncateModes[column] ?? 'end';
48
+ }
49
+ distributeWidths(columns, widths, separatorWidth) {
50
+ const truncatable = columns.filter(col => this.modeFor(col) !== 'none');
51
+ if (truncatable.length === 0)
52
+ return;
53
+ const reserved = columns.filter(col => this.modeFor(col) === 'none').reduce((sum, col) => sum + widths[col], 0);
54
+ const budget = Math.max(0, this.maxWidth - separatorWidth - reserved);
55
+ const original = {};
56
+ for (const col of truncatable)
57
+ original[col] = widths[col];
58
+ const unresolved = new Set(truncatable);
59
+ let remainingBudget = budget;
60
+ let changed = true;
61
+ while (changed && unresolved.size > 0) {
62
+ changed = false;
63
+ const share = Math.floor(remainingBudget / unresolved.size);
64
+ for (const col of unresolved) {
65
+ if (original[col] <= share) {
66
+ widths[col] = original[col];
67
+ remainingBudget -= original[col];
68
+ unresolved.delete(col);
69
+ changed = true;
70
+ }
71
+ }
72
+ }
73
+ if (unresolved.size === 0)
74
+ return;
75
+ const overMin = [...unresolved].reduce((sum, col) => sum + (original[col] - MIN_TRUNCATED_WIDTH), 0);
76
+ const excess = Math.max(0, remainingBudget - unresolved.size * MIN_TRUNCATED_WIDTH);
77
+ let distributed = 0;
78
+ for (const col of unresolved) {
79
+ const share = overMin > 0 ? Math.floor(((original[col] - MIN_TRUNCATED_WIDTH) * excess) / overMin) : 0;
80
+ widths[col] = MIN_TRUNCATED_WIDTH + share;
81
+ distributed += share;
82
+ }
83
+ const leftover = excess - distributed;
84
+ if (leftover > 0)
85
+ widths[[...unresolved][0]] += leftover;
86
+ }
45
87
  toCells() {
46
- const columns = this.columns.filter(col => this.rows.some(row => typeof row[col].value === 'string' || typeof row[col].value === 'number'));
47
- if (this.header) {
88
+ const columns = this.columns.filter(col => this.rows.some(row => isPrintable(row[col]?.value)));
89
+ const rows = [];
90
+ if (this.header && this.rows.length > 0) {
48
91
  const headerRow = {};
49
92
  const linesRow = {};
50
93
  for (const col of columns) {
51
- headerRow[col] = { value: col, align: this.rows[0][col].align === 'right' ? 'center' : 'left' };
52
- linesRow[col] = { value: '', fill: '-' };
94
+ const align = this.rows[0][col]?.align === 'right' ? 'center' : 'left';
95
+ headerRow[col] = { value: col, align, width: col.length };
96
+ linesRow[col] = { value: '', fill: '-', width: 0 };
53
97
  }
54
- this.rows.unshift(linesRow);
55
- this.rows.unshift(headerRow);
98
+ rows.push(headerRow, linesRow);
56
99
  }
57
- const columnWidths = columns.reduce((acc, col) => {
58
- acc[col] = Math.max(...this.rows.map(row => stripVTControlCharacters(row[col]?.formatted ?? String(row[col].value || '')).length));
59
- return acc;
60
- }, {});
61
- const separatorWidth = (columns.length - 1) * COLUMN_SEPARATOR.length;
62
- const totalWidth = Object.values(columnWidths).reduce((sum, width) => sum + width, 0) + separatorWidth;
63
- if (totalWidth > this.maxWidth) {
64
- const reservedWidth = columns
65
- .filter(col => this.noTruncate.includes(col))
66
- .reduce((sum, col) => sum + columnWidths[col], 0);
67
- const truncatableColumns = columns.filter(col => !this.noTruncate.includes(col));
68
- const minWidth = truncatableColumns.length * 4;
69
- const availableWidth = this.maxWidth - separatorWidth - reservedWidth - minWidth;
70
- const truncatableWidth = truncatableColumns.reduce((sum, col) => sum + columnWidths[col], 0) - minWidth;
71
- const reduction = availableWidth / truncatableWidth;
72
- let roundingDiff = availableWidth;
73
- for (const col of truncatableColumns) {
74
- const reducedWidth = MIN_TRUNCATED_WIDTH + Math.floor((columnWidths[col] - MIN_TRUNCATED_WIDTH) * reduction);
75
- columnWidths[col] = reducedWidth;
76
- roundingDiff -= reducedWidth - MIN_TRUNCATED_WIDTH;
77
- }
78
- if (roundingDiff > 0) {
79
- columnWidths[truncatableColumns.length > 0 ? truncatableColumns[0] : columns[0]] += roundingDiff;
100
+ rows.push(...this.rows);
101
+ const columnWidths = {};
102
+ for (const col of columns) {
103
+ let max = 0;
104
+ for (const row of rows) {
105
+ const w = row[col]?.width ?? 0;
106
+ if (w > max)
107
+ max = w;
80
108
  }
109
+ columnWidths[col] = max;
110
+ }
111
+ const separatorWidth = columns.length > 1 ? (columns.length - 1) * COLUMN_SEPARATOR.length : 0;
112
+ const totalWidth = columns.reduce((sum, col) => sum + columnWidths[col], 0) + separatorWidth;
113
+ if (totalWidth > this.maxWidth) {
114
+ this.distributeWidths(columns, columnWidths, separatorWidth);
81
115
  }
82
- return this.rows.map(row => columns.map((col, index) => {
116
+ return rows.map(row => columns.map((col, index) => {
83
117
  const cell = row[col];
84
118
  const width = columnWidths[col];
85
- const fill = cell.fill || ' ';
86
- const padded = pad(String(cell.formatted || cell.value || ''), width, fill, cell.align);
87
- const truncated = this.truncateStart.includes(col) ? truncateStart(padded, width) : truncate(padded, width);
88
- return index === 0 ? truncated : COLUMN_SEPARATOR + truncated;
119
+ const fill = cell?.fill || ' ';
120
+ const display = cell?.formatted ?? toDisplay(cell?.value);
121
+ const padded = pad(display, width, fill, cell?.align);
122
+ const mode = this.modeFor(col);
123
+ const rendered = mode === 'none' ? padded : mode === 'start' ? truncateStart(padded, width) : truncate(padded, width);
124
+ return index === 0 ? rendered : COLUMN_SEPARATOR + rendered;
89
125
  }));
90
126
  }
91
127
  toRows() {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.6.2";
1
+ export declare const version = "6.6.3";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.6.2';
1
+ export const version = '6.6.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.6.2",
3
+ "version": "6.6.3",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",