knip 0.8.2 → 0.9.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
@@ -36,7 +36,7 @@ with OpenAI_</sup>
36
36
 
37
37
  npm install -D knip
38
38
 
39
- Knip requires at least Node.js v16.17 or v18. Knip is _cutting edge!_
39
+ Knip supports LTS versions of Node.js, and currently requires at least Node.js v16.17 or v18.3. Knip is _cutting edge!_
40
40
 
41
41
  ## Usage
42
42
 
@@ -272,15 +272,18 @@ configuration can be tweaked further to the project structure.
272
272
 
273
273
  ## Reporters
274
274
 
275
- For starters, Knip already contains a few useful reporters:
275
+ Knip provides the following built-in reporters:
276
276
 
277
- - `symbol` (default)
278
- - `compact`
279
- - `codeowners`
277
+ - [`json`](#json)
278
+ - [`symbol`](#symbol-default) (default)
279
+ - [`compact`](#compact)
280
+ - [`codeowners`](#code-owners)
280
281
 
281
282
  ### Custom Reporters
282
283
 
283
- When a `--reporter ./my-reporter` is passed, the default export of that module should have this interface:
284
+ When the provided built-in reporters are not quite sufficient, a custom reporter can be implemented.
285
+
286
+ Pass `--reporter ./my-reporter`, with the default export of that module having this interface:
284
287
 
285
288
  ```ts
286
289
  type Reporter = (options: ReporterOptions) => void;
@@ -297,7 +300,59 @@ type ReporterOptions = {
297
300
 
298
301
  The data can then be used to write issues to `stdout`, a JSON or CSV file, or sent to a service, anything really!
299
302
 
300
- ### Example Output
303
+ ### JSON
304
+
305
+ The `json` reporter output is meant to be consumed by other tools. It reports in JSON format as an array with one object
306
+ per file like this:
307
+
308
+ ```json
309
+ [
310
+ {
311
+ "file": "src/Registration.tsx",
312
+ "owners": ["@org/owner"],
313
+ "files": true,
314
+ "unlisted": ["react"],
315
+ "exports": ["lowercaseFirstLetter", "RegistrationBox"],
316
+ "types": ["RegistrationServices", "RegistrationAction"],
317
+ "duplicates": ["Registration", "default"]
318
+ }
319
+ ]
320
+ ```
321
+
322
+ The keys match the [known issue types](#reading-the-report).
323
+
324
+ #### Usage Ideas
325
+
326
+ Use tools like [miller](https://github.com/johnkerl/miller) or [jtbl](https://github.com/kellyjonbrazil/jtbl) to consume
327
+ the JSON and render a table in the terminal.
328
+
329
+ ##### Table
330
+
331
+ ```
332
+ $ npx knip --reporter json | mlr --ijson --opprint --no-auto-flatten cat
333
+ file owners files unlisted exports types duplicates
334
+ src/Registration.tsx @org/owner true react lowercaseFirstLetter, RegistrationBox RegistrationServices, RegistrationAction Registration, default
335
+ src/ProductsList.tsx @org/team false - - ProductDetail -
336
+ ```
337
+
338
+ ##### Markdown Table
339
+
340
+ ```
341
+ $ npx knip --reporter json | mlr --ijson --omd --no-auto-flatten cat
342
+ | file | owners | files | duplicates |
343
+ | --- | --- | --- | --- |
344
+ | src/Registration.tsx | @org/owner | true | Registration, default |
345
+ | src/ProductsList.tsx | @org/team | false | |
346
+ ```
347
+
348
+ Include specific issue types and/or replace the `cat` command with `put` for clean output:
349
+
350
+ ```
351
+ npx knip --include files,duplicates --reporter json | mlr --ijson --opprint --no-auto-flatten put 'for (e in $*) { if(is_array($[e])) { $[e] = joinv($[e], ", ") } }'
352
+ npx knip --reporter json | mlr --ijson --omd --no-auto-flatten put 'for (e in $*) { if(is_array($[e])) { $[e] = joinv($[e], ", ") } }'
353
+ ```
354
+
355
+ ### More Output Examples
301
356
 
302
357
  #### Symbol (default)
303
358
 
package/dist/cli.js CHANGED
@@ -59,10 +59,10 @@ const run = async () => {
59
59
  level: isDebug ? Number(debugLevel) : 0,
60
60
  },
61
61
  });
62
- printReport({ report, issues, cwd, workingDir, isDev, options: reporterOptions });
62
+ await printReport({ report, issues, cwd, workingDir, isDev, options: reporterOptions });
63
63
  const totalErrorCount = Object.keys(report)
64
- .filter((reportGroup) => report[reportGroup])
65
- .map(reportGroup => reportGroup === 'unlisted' ? 'unresolved' : reportGroup)
64
+ .filter(reportGroup => report[reportGroup])
65
+ .map(reportGroup => (reportGroup === 'unlisted' ? 'unresolved' : reportGroup))
66
66
  .reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
67
67
  if (totalErrorCount > Number(maxIssues))
68
68
  process.exit(totalErrorCount);
@@ -2,5 +2,6 @@ declare const _default: {
2
2
  symbols: ({ report, issues, workingDir, isDev }: import("../types").ReporterOptions) => void;
3
3
  compact: ({ report, issues, workingDir, isDev }: import("../types").ReporterOptions) => void;
4
4
  codeowners: ({ report, issues, cwd, isDev, options }: import("../types").ReporterOptions) => void;
5
+ json: ({ report, issues, cwd, options }: import("../types").ReporterOptions) => Promise<void>;
5
6
  };
6
7
  export default _default;
@@ -6,8 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const symbols_1 = __importDefault(require("./symbols"));
7
7
  const compact_1 = __importDefault(require("./compact"));
8
8
  const codeowners_1 = __importDefault(require("./codeowners"));
9
+ const json_1 = __importDefault(require("./json"));
9
10
  exports.default = {
10
11
  symbols: symbols_1.default,
11
12
  compact: compact_1.default,
12
13
  codeowners: codeowners_1.default,
14
+ json: json_1.default,
13
15
  };
@@ -0,0 +1,3 @@
1
+ import type { ReporterOptions } from '../types';
2
+ declare const _default: ({ report, issues, cwd, options }: ReporterOptions) => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_path_1 = __importDefault(require("node:path"));
7
+ const fs_1 = require("../util/fs");
8
+ const ownership_1 = require("@snyk/github-codeowners/dist/lib/ownership");
9
+ exports.default = async ({ report, issues, cwd, options }) => {
10
+ let opts = {};
11
+ try {
12
+ opts = options ? JSON.parse(options) : opts;
13
+ }
14
+ catch (error) {
15
+ console.error(error);
16
+ }
17
+ const json = {};
18
+ const codeownersFilePath = node_path_1.default.resolve(opts.codeowners ?? '.github/CODEOWNERS');
19
+ const codeownersEngine = (await (0, fs_1.isFile)(codeownersFilePath)) && ownership_1.OwnershipEngine.FromCodeownersFile(codeownersFilePath);
20
+ const flatten = (issues) => Object.values(issues).map(Object.values).flat();
21
+ const initRow = (filePath) => {
22
+ const file = node_path_1.default.relative(cwd, filePath);
23
+ const row = {
24
+ file,
25
+ ...(codeownersEngine && { owners: codeownersEngine.calcFileOwnership(file) }),
26
+ ...(report.files && { files: false }),
27
+ ...(report.unlisted && { unlisted: [] }),
28
+ ...((report.exports || report.nsExports) && { exports: [] }),
29
+ ...((report.types || report.nsTypes) && { types: [] }),
30
+ ...(report.duplicates && { duplicates: [] }),
31
+ };
32
+ return row;
33
+ };
34
+ if (report.files) {
35
+ issues.files.forEach(filePath => {
36
+ json[filePath] = json[filePath] ?? initRow(filePath);
37
+ json[filePath].files = true;
38
+ });
39
+ }
40
+ if (report.unlisted) {
41
+ flatten(issues.unresolved).forEach(({ filePath, symbol }) => {
42
+ json[filePath] = json[filePath] ?? initRow(filePath);
43
+ json[filePath].unlisted?.push(symbol);
44
+ });
45
+ }
46
+ if (report.exports) {
47
+ flatten(issues.exports).forEach(({ filePath, symbol }) => {
48
+ json[filePath] = json[filePath] ?? initRow(filePath);
49
+ json[filePath].exports?.push(symbol);
50
+ });
51
+ }
52
+ if (report.nsExports) {
53
+ flatten(issues.nsExports).forEach(({ filePath, symbol }) => {
54
+ json[filePath] = json[filePath] ?? initRow(filePath);
55
+ json[filePath].exports?.push(symbol);
56
+ });
57
+ }
58
+ if (report.types) {
59
+ flatten(issues.types).forEach(({ filePath, symbol }) => {
60
+ json[filePath] = json[filePath] ?? initRow(filePath);
61
+ json[filePath].types = json[filePath].types ?? [];
62
+ json[filePath].types?.push(symbol);
63
+ });
64
+ }
65
+ if (report.nsTypes) {
66
+ flatten(issues.nsTypes).forEach(({ filePath, symbol }) => {
67
+ json[filePath] = json[filePath] ?? initRow(filePath);
68
+ json[filePath].types?.push(symbol);
69
+ });
70
+ }
71
+ if (report.duplicates) {
72
+ flatten(issues.duplicates).forEach(({ filePath, symbols }) => {
73
+ json[filePath] = json[filePath] ?? initRow(filePath);
74
+ json[filePath].duplicates?.push(...symbols);
75
+ });
76
+ }
77
+ console.log(JSON.stringify(Object.values(json)));
78
+ };
package/dist/util/fs.d.ts CHANGED
@@ -1 +1,2 @@
1
+ export declare const isFile: (filePath: string) => Promise<boolean>;
1
2
  export declare const findFile: (cwd: string, workingDir: string, fileName: string) => Promise<string | undefined>;
package/dist/util/fs.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.findFile = void 0;
6
+ exports.findFile = exports.isFile = void 0;
7
7
  const promises_1 = __importDefault(require("node:fs/promises"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const isFile = async (filePath) => {
@@ -15,9 +15,10 @@ const isFile = async (filePath) => {
15
15
  return false;
16
16
  }
17
17
  };
18
+ exports.isFile = isFile;
18
19
  const findFile = async (cwd, workingDir, fileName) => {
19
20
  const filePath = node_path_1.default.join(workingDir, fileName);
20
- if (await isFile(filePath)) {
21
+ if (await (0, exports.isFile)(filePath)) {
21
22
  return filePath;
22
23
  }
23
24
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript project",
5
5
  "keywords": [
6
6
  "find",
@@ -49,10 +49,10 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/micromatch": "4.0.2",
52
- "@types/node": "18.8.5",
52
+ "@types/node": "18.11.2",
53
53
  "prettier": "2.7.1",
54
54
  "release-it": "15.5.0",
55
- "tsx": "3.10.1",
55
+ "tsx": "3.10.3",
56
56
  "typescript": "4.8.4"
57
57
  },
58
58
  "release-it": {
@@ -61,7 +61,7 @@
61
61
  }
62
62
  },
63
63
  "engines": {
64
- "node": ">=16.17.0"
64
+ "node": ">=16.17.0 <17 || >=18.3.0"
65
65
  },
66
66
  "engineStrict": true
67
67
  }