knip 2.36.0 → 2.37.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.
package/README.md CHANGED
@@ -704,7 +704,7 @@ $ npx knip --help
704
704
  -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)
705
705
  --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated
706
706
  --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)
707
- --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)
707
+ --reporter Select reporter: symbols, compact, codeowners, json, jsonExt, can be repeated (default: symbols)
708
708
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
709
709
  --no-config-hints Suppress configuration hints
710
710
  --no-exit-code Always exit with code zero (0)
@@ -58,7 +58,12 @@ export declare class ProjectPrincipal {
58
58
  external: boolean;
59
59
  internal: boolean;
60
60
  };
61
- findUnusedMembers(filePath: string, members: ExportItemMember[]): string[];
61
+ findUnusedMembers(filePath: string, members: ExportItemMember[]): ExportItemMember[];
62
62
  private findReferences;
63
+ getPos(node: ts.Node, pos: number): {
64
+ pos: number;
65
+ line: number;
66
+ col: number;
67
+ };
63
68
  }
64
69
  export {};
@@ -2,7 +2,7 @@ import { isGitIgnoredSync } from 'globby';
2
2
  import ts from 'typescript';
3
3
  import { DEFAULT_EXTENSIONS } from './constants.js';
4
4
  import { IGNORED_FILE_EXTENSIONS } from './constants.js';
5
- import { getJSDocTags, isInModuleBlock } from './typescript/ast-helpers.js';
5
+ import { getJSDocTags, getLineAndCharacterOfPosition, isInModuleBlock } from './typescript/ast-helpers.js';
6
6
  import { createHosts } from './typescript/createHosts.js';
7
7
  import { getImportsAndExports } from './typescript/getImportsAndExports.js';
8
8
  import { SourceFileManager } from './typescript/SourceFileManager.js';
@@ -176,8 +176,7 @@ export class ProjectPrincipal {
176
176
  return hasReferences;
177
177
  }
178
178
  findUnusedMembers(filePath, members) {
179
- return members
180
- .filter(member => {
179
+ return members.filter(member => {
181
180
  if (getJSDocTags(member.node).has('@public'))
182
181
  return false;
183
182
  const referencedSymbols = this.findReferences(filePath, member.pos);
@@ -188,8 +187,7 @@ export class ProjectPrincipal {
188
187
  const internalRefs = files.filter(f => f === filePath);
189
188
  const externalRefs = files.filter(f => f !== filePath);
190
189
  return externalRefs.length === 0 && internalRefs.length === 0;
191
- })
192
- .map(member => member.identifier);
190
+ });
193
191
  }
194
192
  findReferences(filePath, pos) {
195
193
  try {
@@ -199,4 +197,8 @@ export class ProjectPrincipal {
199
197
  return [];
200
198
  }
201
199
  }
200
+ getPos(node, pos) {
201
+ const { line, character } = getLineAndCharacterOfPosition(node, pos);
202
+ return { pos, line: line + 1, col: character + 1 };
203
+ }
202
204
  }
package/dist/index.js CHANGED
@@ -304,12 +304,24 @@ export const main = async (unresolvedConfiguration) => {
304
304
  if (isProduction)
305
305
  continue;
306
306
  principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
307
- collector.addIssue({ type: 'enumMembers', filePath, symbol: member, parentSymbol: symbol });
307
+ collector.addIssue({
308
+ type: 'enumMembers',
309
+ filePath,
310
+ symbol: member.identifier,
311
+ parentSymbol: symbol,
312
+ ...principal.getPos(member.node, member.pos),
313
+ });
308
314
  });
309
315
  }
310
316
  if (report.classMembers && exportedItem.type === 'class' && exportedItem.members) {
311
317
  principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
312
- collector.addIssue({ type: 'classMembers', filePath, symbol: member, parentSymbol: symbol });
318
+ collector.addIssue({
319
+ type: 'classMembers',
320
+ filePath,
321
+ symbol: member.identifier,
322
+ parentSymbol: symbol,
323
+ ...principal.getPos(member.node, member.pos),
324
+ });
313
325
  });
314
326
  }
315
327
  continue;
@@ -321,11 +333,22 @@ export const main = async (unresolvedConfiguration) => {
321
333
  if (isProduction)
322
334
  continue;
323
335
  const type = isStar ? 'nsTypes' : 'types';
324
- collector.addIssue({ type, filePath, symbol, symbolType: exportedItem.type });
336
+ collector.addIssue({
337
+ type,
338
+ filePath,
339
+ symbol,
340
+ symbolType: exportedItem.type,
341
+ ...principal.getPos(exportedItem.node, exportedItem.pos),
342
+ });
325
343
  }
326
344
  else {
327
345
  const type = isStar ? 'nsExports' : 'exports';
328
- collector.addIssue({ type, filePath, symbol });
346
+ collector.addIssue({
347
+ type,
348
+ filePath,
349
+ symbol,
350
+ ...principal.getPos(exportedItem.node, exportedItem.pos),
351
+ });
329
352
  }
330
353
  }
331
354
  }
@@ -3,5 +3,6 @@ declare const _default: {
3
3
  compact: ({ report, issues, isShowProgress }: import("../index.js").ReporterOptions) => void;
4
4
  codeowners: ({ report, issues, isShowProgress, options }: import("../index.js").ReporterOptions) => void;
5
5
  json: ({ report, issues, options }: import("../index.js").ReporterOptions) => Promise<void>;
6
+ jsonExt: ({ report, issues, options }: import("../index.js").ReporterOptions) => Promise<void>;
6
7
  };
7
8
  export default _default;
@@ -1,10 +1,12 @@
1
1
  import codeowners from './codeowners.js';
2
2
  import compact from './compact.js';
3
3
  import json from './json.js';
4
+ import jsonExt from './jsonExt.js';
4
5
  import symbols from './symbols.js';
5
6
  export default {
6
7
  symbols,
7
8
  compact,
8
9
  codeowners,
9
10
  json,
11
+ jsonExt,
10
12
  };
@@ -0,0 +1,3 @@
1
+ import type { ReporterOptions } from '../types/issues.js';
2
+ declare const _default: ({ report, issues, options }: ReporterOptions) => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,73 @@
1
+ import { OwnershipEngine } from '@snyk/github-codeowners/dist/lib/ownership/index.js';
2
+ import { isFile } from '../util/fs.js';
3
+ import { relative, resolve } from '../util/path.js';
4
+ const mergeTypes = (type) => type === 'exports' || type === 'nsExports' ? 'exports' : type === 'types' || type === 'nsTypes' ? 'types' : type;
5
+ export default async ({ report, issues, options }) => {
6
+ let opts = {};
7
+ try {
8
+ opts = options ? JSON.parse(options) : opts;
9
+ }
10
+ catch (error) {
11
+ console.error(error);
12
+ }
13
+ const json = {};
14
+ const codeownersFilePath = resolve(opts.codeowners ?? '.github/CODEOWNERS');
15
+ const codeownersEngine = isFile(codeownersFilePath) && OwnershipEngine.FromCodeownersFile(codeownersFilePath);
16
+ const flatten = (issues) => Object.values(issues).flatMap(Object.values);
17
+ const initRow = (filePath) => {
18
+ const file = relative(filePath);
19
+ const row = {
20
+ file,
21
+ ...(codeownersEngine && { owners: codeownersEngine.calcFileOwnership(file) }),
22
+ ...(report.files && { files: false }),
23
+ ...(report.dependencies && { dependencies: [] }),
24
+ ...(report.devDependencies && { devDependencies: [] }),
25
+ ...(report.optionalPeerDependencies && { optionalPeerDependencies: [] }),
26
+ ...(report.unlisted && { unlisted: [] }),
27
+ ...(report.binaries && { binaries: [] }),
28
+ ...(report.unresolved && { unresolved: [] }),
29
+ ...((report.exports || report.nsExports) && { exports: [] }),
30
+ ...((report.types || report.nsTypes) && { types: [] }),
31
+ ...(report.enumMembers && { enumMembers: {} }),
32
+ ...(report.classMembers && { classMembers: {} }),
33
+ ...(report.duplicates && { duplicates: [] }),
34
+ };
35
+ return row;
36
+ };
37
+ for (const [reportType, isReportType] of Object.entries(report)) {
38
+ if (isReportType) {
39
+ if (reportType === 'files') {
40
+ Array.from(issues[reportType]).forEach(filePath => {
41
+ json[filePath] = json[filePath] ?? initRow(filePath);
42
+ json[filePath][reportType] = true;
43
+ });
44
+ }
45
+ else {
46
+ const type = mergeTypes(reportType);
47
+ flatten(issues[reportType]).forEach(issue => {
48
+ const { filePath, symbol, symbols, parentSymbol } = issue;
49
+ json[filePath] = json[filePath] ?? initRow(filePath);
50
+ if (type === 'duplicates') {
51
+ symbols && json[filePath][type]?.push(symbols.map(symbol => ({ name: symbol })));
52
+ }
53
+ else if (type === 'enumMembers' || type === 'classMembers') {
54
+ const item = json[filePath][type];
55
+ if (parentSymbol && item) {
56
+ item[parentSymbol] = item[parentSymbol] ?? [];
57
+ item[parentSymbol].push({ name: issue.symbol, line: issue.line, col: issue.col, pos: issue.pos });
58
+ }
59
+ }
60
+ else {
61
+ if (type === 'exports' || type === 'types') {
62
+ json[filePath][type]?.push({ name: issue.symbol, line: issue.line, col: issue.col, pos: issue.pos });
63
+ }
64
+ else {
65
+ json[filePath][type]?.push({ name: symbol });
66
+ }
67
+ }
68
+ });
69
+ }
70
+ }
71
+ }
72
+ console.log(JSON.stringify(Object.values(json)));
73
+ };
@@ -15,6 +15,9 @@ export type Issue = {
15
15
  symbolType?: SymbolType;
16
16
  parentSymbol?: string;
17
17
  severity?: IssueSeverity;
18
+ pos?: number;
19
+ line?: number;
20
+ col?: number;
18
21
  };
19
22
  export type IssueSet = Set<string>;
20
23
  export type IssueRecords = Record<string, Record<string, Issue>>;
@@ -25,4 +25,5 @@ export declare function findDescendants<T>(node: ts.Node | undefined, callback:
25
25
  export declare const isDeclarationFileExtension: (extension: string) => boolean;
26
26
  export declare const isInModuleBlock: (node: ts.Node) => boolean;
27
27
  export declare const getJSDocTags: (node: ts.Node) => Set<string>;
28
+ export declare const getLineAndCharacterOfPosition: (node: ts.Node, pos: number) => ts.LineAndCharacter;
28
29
  export {};
@@ -110,3 +110,6 @@ export const getJSDocTags = (node) => {
110
110
  }
111
111
  return tags;
112
112
  };
113
+ export const getLineAndCharacterOfPosition = (node, pos) => {
114
+ return node.getSourceFile().getLineAndCharacterOfPosition(pos);
115
+ };
@@ -95,10 +95,10 @@ export const getImportsAndExports = (sourceFile, options) => {
95
95
  const item = exports.get(identifier);
96
96
  const crew = [...item.members, ...members];
97
97
  const tags = new Set([...item.jsDocTags, ...jsDocTags]);
98
- exports.set(identifier, { ...item, node, type, pos, members: crew, jsDocTags: tags });
98
+ exports.set(identifier, { ...item, members: crew, jsDocTags: tags });
99
99
  }
100
100
  else {
101
- exports.set(identifier, { node, type, pos, members, jsDocTags });
101
+ exports.set(identifier, { node, type, members, jsDocTags, pos });
102
102
  }
103
103
  if (!jsDocTags.has('@alias')) {
104
104
  if (ts.isExportAssignment(node))
@@ -1,4 +1,4 @@
1
- export declare const helpText = "\u2702\uFE0F Find unused files, dependencies and exports in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -c, --config [file] Configuration file path (default: [.]knip.json[c], knip.js, knip.ts or package.json#knip)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)\n -W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)\n --directory [dir] Run process from a different directory (default: cwd)\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --dependencies Shortcut for --include dependencies,unlisted,unresolved\n --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates\n --include-entry-exports Include entry files when reporting unused exports\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n -d, --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure count and running time of expensive functions and display stats table\n -h, --help Print this help text\n -V, --version Print version\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore documentation and bug reports: https://github.com/webpro/knip";
1
+ export declare const helpText = "\u2702\uFE0F Find unused files, dependencies and exports in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -c, --config [file] Configuration file path (default: [.]knip.json[c], knip.js, knip.ts or package.json#knip)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)\n -W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)\n --directory [dir] Run process from a different directory (default: cwd)\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --dependencies Shortcut for --include dependencies,unlisted,unresolved\n --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates\n --include-entry-exports Include entry files when reporting unused exports\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n --reporter Select reporter: symbols, compact, codeowners, json, jsonExt, can be repeated (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n -d, --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure count and running time of expensive functions and display stats table\n -h, --help Print this help text\n -V, --version Print version\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore documentation and bug reports: https://github.com/webpro/knip";
2
2
  declare const _default: {
3
3
  config: string | undefined;
4
4
  debug: boolean | undefined;
@@ -20,7 +20,7 @@ Options:
20
20
  -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)
21
21
  --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated
22
22
  --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)
23
- --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)
23
+ --reporter Select reporter: symbols, compact, codeowners, json, jsonExt, can be repeated (default: symbols)
24
24
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
25
25
  --no-config-hints Suppress configuration hints
26
26
  --no-exit-code Always exit with code zero (0)
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.36.0";
1
+ export declare const version = "2.37.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.36.0';
1
+ export const version = '2.37.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "2.36.0",
3
+ "version": "2.37.0",
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",