knip 0.8.1 → 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 +62 -7
- package/dist/cli.js +8 -9
- package/dist/index.d.ts +1 -12
- package/dist/index.js +6 -0
- package/dist/progress.d.ts +5 -0
- package/dist/progress.js +41 -0
- package/dist/reporters/index.d.ts +1 -0
- package/dist/reporters/index.js +2 -0
- package/dist/reporters/json.d.ts +3 -0
- package/dist/reporters/json.js +78 -0
- package/dist/reporters/symbols.js +9 -6
- package/dist/runner.d.ts +2 -13
- package/dist/runner.js +9 -27
- package/dist/types.d.ts +1 -0
- package/dist/util/fs.d.ts +1 -0
- package/dist/util/fs.js +3 -2
- package/package.json +5 -5
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
|
-
|
|
275
|
+
Knip provides the following built-in reporters:
|
|
276
276
|
|
|
277
|
-
- `
|
|
278
|
-
- `
|
|
279
|
-
- `
|
|
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
|
|
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
|
-
###
|
|
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
|
@@ -37,7 +37,7 @@ if (help) {
|
|
|
37
37
|
}
|
|
38
38
|
const cwd = process.cwd();
|
|
39
39
|
const workingDir = dir ? node_path_1.default.resolve(dir) : cwd;
|
|
40
|
-
const isShowProgress = noProgress === false
|
|
40
|
+
const isShowProgress = !isDebug && noProgress === false && process.stdout.isTTY && typeof process.stdout.cursorTo === 'function';
|
|
41
41
|
const printReport = reporter in reporters_1.default ? reporters_1.default[reporter] : require(node_path_1.default.join(workingDir, reporter));
|
|
42
42
|
const run = async () => {
|
|
43
43
|
try {
|
|
@@ -59,14 +59,13 @@ const run = async () => {
|
|
|
59
59
|
level: isDebug ? Number(debugLevel) : 0,
|
|
60
60
|
},
|
|
61
61
|
});
|
|
62
|
-
printReport({ report, issues, cwd, workingDir, isDev, options: reporterOptions });
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
62
|
+
await printReport({ report, issues, cwd, workingDir, isDev, options: reporterOptions });
|
|
63
|
+
const totalErrorCount = Object.keys(report)
|
|
64
|
+
.filter(reportGroup => report[reportGroup])
|
|
65
|
+
.map(reportGroup => (reportGroup === 'unlisted' ? 'unresolved' : reportGroup))
|
|
66
|
+
.reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
|
|
67
|
+
if (totalErrorCount > Number(maxIssues))
|
|
68
|
+
process.exit(totalErrorCount);
|
|
70
69
|
}
|
|
71
70
|
catch (error) {
|
|
72
71
|
if (error instanceof errors_1.ConfigurationError) {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,16 +2,5 @@ import type { UnresolvedConfiguration } from './types';
|
|
|
2
2
|
export declare const main: (options: UnresolvedConfiguration) => Promise<{
|
|
3
3
|
report: import("./types").Report;
|
|
4
4
|
issues: import("./types").Issues;
|
|
5
|
-
counters:
|
|
6
|
-
files: number;
|
|
7
|
-
dependencies: number;
|
|
8
|
-
devDependencies: number;
|
|
9
|
-
unresolved: number;
|
|
10
|
-
exports: number;
|
|
11
|
-
types: number;
|
|
12
|
-
nsExports: number;
|
|
13
|
-
nsTypes: number;
|
|
14
|
-
duplicates: number;
|
|
15
|
-
processed: number;
|
|
16
|
-
};
|
|
5
|
+
counters: import("./types").Counters;
|
|
17
6
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -13,9 +13,12 @@ const project_1 = require("./util/project");
|
|
|
13
13
|
const runner_1 = require("./runner");
|
|
14
14
|
const errors_1 = require("./util/errors");
|
|
15
15
|
const debug_1 = require("./util/debug");
|
|
16
|
+
const progress_1 = require("./progress");
|
|
16
17
|
const main = async (options) => {
|
|
17
18
|
const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } = options;
|
|
19
|
+
const updateMessage = (0, progress_1.getMessageUpdater)(options);
|
|
18
20
|
(0, debug_1.debugLogObject)(options, 1, 'Unresolved onfiguration', options);
|
|
21
|
+
updateMessage('Reading configuration and manifest files...');
|
|
19
22
|
const manifestPath = await (0, fs_1.findFile)(cwd, workingDir, 'package.json');
|
|
20
23
|
const manifest = manifestPath && require(manifestPath);
|
|
21
24
|
const configFilePath = configFilePathArg ?? 'knip.json';
|
|
@@ -51,6 +54,7 @@ const main = async (options) => {
|
|
|
51
54
|
const projectOptions = resolvedTsConfigFilePath
|
|
52
55
|
? { tsConfigFilePath: resolvedTsConfigFilePath }
|
|
53
56
|
: { compilerOptions: { allowJs: true } };
|
|
57
|
+
updateMessage('Resolving entry files...');
|
|
54
58
|
const entryPaths = await (0, path_1.resolvePaths)({
|
|
55
59
|
cwd,
|
|
56
60
|
workingDir,
|
|
@@ -65,6 +69,7 @@ const main = async (options) => {
|
|
|
65
69
|
production.resolveSourceFileDependencies();
|
|
66
70
|
const productionFiles = production.getSourceFiles();
|
|
67
71
|
(0, debug_1.debugLogSourceFiles)(options, 1, 'Included production source files', productionFiles);
|
|
72
|
+
updateMessage('Resolving project files...');
|
|
68
73
|
const projectPaths = await (0, path_1.resolvePaths)({
|
|
69
74
|
cwd,
|
|
70
75
|
workingDir,
|
|
@@ -79,6 +84,7 @@ const main = async (options) => {
|
|
|
79
84
|
return { entryFiles, productionFiles, projectFiles };
|
|
80
85
|
}
|
|
81
86
|
else {
|
|
87
|
+
updateMessage('Resolving project files...');
|
|
82
88
|
const project = (0, project_1.createProject)({ tsConfigFilePath: resolvedTsConfigFilePath });
|
|
83
89
|
const files = project.getSourceFiles();
|
|
84
90
|
return { entryFiles: files, productionFiles: files, projectFiles: files };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Configuration, Counters, Issue } from './types';
|
|
2
|
+
export declare const getMessageUpdater: (configuration: {
|
|
3
|
+
isShowProgress: boolean;
|
|
4
|
+
}) => (message: string) => void;
|
|
5
|
+
export declare const getCountersUpdater: (configuration: Configuration, counters: Counters) => (issue?: Issue) => void;
|
package/dist/progress.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
exports.getCountersUpdater = exports.getMessageUpdater = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const log_1 = require("./log");
|
|
9
|
+
const lineRewriter = new log_1.LineRewriter();
|
|
10
|
+
const getMessageUpdater = (configuration) => {
|
|
11
|
+
const { isShowProgress } = configuration;
|
|
12
|
+
if (!isShowProgress)
|
|
13
|
+
return () => { };
|
|
14
|
+
return (message) => lineRewriter.update([message]);
|
|
15
|
+
};
|
|
16
|
+
exports.getMessageUpdater = getMessageUpdater;
|
|
17
|
+
const getCountersUpdater = (configuration, counters) => {
|
|
18
|
+
const { workingDir, isShowProgress, report } = configuration;
|
|
19
|
+
if (!isShowProgress)
|
|
20
|
+
return () => { };
|
|
21
|
+
return (issue) => {
|
|
22
|
+
if (!issue)
|
|
23
|
+
return lineRewriter.resetLines();
|
|
24
|
+
const { processed, total } = counters;
|
|
25
|
+
const percentage = Math.floor((processed / total) * 100);
|
|
26
|
+
const messages = [(0, log_1.getLine)(`${percentage}%`, `of files processed (${processed} of ${total})`)];
|
|
27
|
+
report.files && messages.push((0, log_1.getLine)(counters.files, 'unused files'));
|
|
28
|
+
report.unlisted && messages.push((0, log_1.getLine)(counters.unresolved, 'unlisted dependencies'));
|
|
29
|
+
report.exports && messages.push((0, log_1.getLine)(counters.exports, 'unused exports'));
|
|
30
|
+
report.nsExports && messages.push((0, log_1.getLine)(counters.nsExports, 'unused exports in namespace'));
|
|
31
|
+
report.types && messages.push((0, log_1.getLine)(counters.types, 'unused types'));
|
|
32
|
+
report.nsTypes && messages.push((0, log_1.getLine)(counters.nsTypes, 'unused types in namespace'));
|
|
33
|
+
report.duplicates && messages.push((0, log_1.getLine)(counters.duplicates, 'duplicate exports'));
|
|
34
|
+
if (processed < total) {
|
|
35
|
+
messages.push('');
|
|
36
|
+
messages.push(`Processing: ${node_path_1.default.relative(workingDir, issue.filePath)}`);
|
|
37
|
+
}
|
|
38
|
+
lineRewriter.update(messages);
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
exports.getCountersUpdater = getCountersUpdater;
|
|
@@ -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;
|
package/dist/reporters/index.js
CHANGED
|
@@ -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,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
|
+
};
|
|
@@ -4,9 +4,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const node_path_1 = __importDefault(require("node:path"));
|
|
7
|
-
const
|
|
7
|
+
const TRUNCATE_WIDTH = 40;
|
|
8
|
+
const logIssueLine = (issue, workingDir, maxWidth) => {
|
|
8
9
|
const symbols = issue.symbols ? issue.symbols.join(', ') : issue.symbol;
|
|
9
|
-
|
|
10
|
+
const truncatedSymbol = symbols.length > maxWidth ? symbols.slice(0, maxWidth - 3) + '...' : symbols;
|
|
11
|
+
const filePath = node_path_1.default.relative(workingDir, issue.filePath);
|
|
12
|
+
console.log(`${truncatedSymbol.padEnd(maxWidth + 2)}${issue.symbolType?.padEnd(11) || ''}${filePath}`);
|
|
10
13
|
};
|
|
11
14
|
const logIssueGroupResult = (issues, workingDir, title) => {
|
|
12
15
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
@@ -17,12 +20,12 @@ const logIssueGroupResult = (issues, workingDir, title) => {
|
|
|
17
20
|
console.log('Not found');
|
|
18
21
|
}
|
|
19
22
|
};
|
|
20
|
-
const logIssueGroupResults = (issues, workingDir, title) => {
|
|
23
|
+
const logIssueGroupResults = (issues, workingDir, title, isTruncate = false) => {
|
|
21
24
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
22
25
|
if (issues.length) {
|
|
23
26
|
const sortedByFilePath = issues.sort((a, b) => (a.filePath > b.filePath ? 1 : -1));
|
|
24
|
-
const
|
|
25
|
-
sortedByFilePath.forEach(issue => logIssueLine(
|
|
27
|
+
const maxWidth = isTruncate ? TRUNCATE_WIDTH : issues.reduce((max, issue) => Math.max(issue.symbol.length, max), 0);
|
|
28
|
+
sortedByFilePath.forEach(issue => logIssueLine(issue, workingDir, maxWidth));
|
|
26
29
|
}
|
|
27
30
|
else {
|
|
28
31
|
console.log('Not found');
|
|
@@ -64,6 +67,6 @@ exports.default = ({ report, issues, workingDir, isDev }) => {
|
|
|
64
67
|
}
|
|
65
68
|
if (report.duplicates) {
|
|
66
69
|
const unreferencedDuplicates = Object.values(issues.duplicates).map(Object.values).flat();
|
|
67
|
-
logIssueGroupResults(unreferencedDuplicates, workingDir, reportMultipleGroups && 'DUPLICATE EXPORTS');
|
|
70
|
+
logIssueGroupResults(unreferencedDuplicates, workingDir, reportMultipleGroups && 'DUPLICATE EXPORTS', true);
|
|
68
71
|
}
|
|
69
72
|
};
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
import type { Configuration, Issues } from './types';
|
|
1
|
+
import type { Configuration, Issues, Counters } from './types';
|
|
2
2
|
export declare function findIssues(configuration: Configuration): Promise<{
|
|
3
3
|
issues: Issues;
|
|
4
|
-
counters:
|
|
5
|
-
files: number;
|
|
6
|
-
dependencies: number;
|
|
7
|
-
devDependencies: number;
|
|
8
|
-
unresolved: number;
|
|
9
|
-
exports: number;
|
|
10
|
-
types: number;
|
|
11
|
-
nsExports: number;
|
|
12
|
-
nsTypes: number;
|
|
13
|
-
duplicates: number;
|
|
14
|
-
processed: number;
|
|
15
|
-
};
|
|
4
|
+
counters: Counters;
|
|
16
5
|
}>;
|
package/dist/runner.js
CHANGED
|
@@ -10,12 +10,12 @@ const ts_morph_helpers_1 = require("ts-morph-helpers");
|
|
|
10
10
|
const project_1 = require("./util/project");
|
|
11
11
|
const type_1 = require("./util/type");
|
|
12
12
|
const dependencies_1 = require("./util/dependencies");
|
|
13
|
-
const log_1 = require("./log");
|
|
14
13
|
const debug_1 = require("./util/debug");
|
|
15
|
-
const
|
|
14
|
+
const progress_1 = require("./progress");
|
|
16
15
|
async function findIssues(configuration) {
|
|
17
|
-
const { workingDir,
|
|
16
|
+
const { workingDir, report, isDev, jsDocOptions } = configuration;
|
|
18
17
|
const { entryFiles, productionFiles, projectFiles, isIncludeEntryFiles } = configuration;
|
|
18
|
+
const updateMessage = (0, progress_1.getMessageUpdater)(configuration);
|
|
19
19
|
const { getUnresolvedDependencies, getUnusedDependencies, getUnusedDevDependencies } = (0, dependencies_1.getDependencyAnalyzer)(configuration);
|
|
20
20
|
const [usedProductionFiles, unreferencedProductionFiles] = (0, project_1.partitionSourceFiles)(projectFiles, productionFiles);
|
|
21
21
|
const [usedEntryFiles, usedNonEntryFiles] = (0, project_1.partitionSourceFiles)(usedProductionFiles, entryFiles);
|
|
@@ -45,42 +45,25 @@ async function findIssues(configuration) {
|
|
|
45
45
|
nsTypes: 0,
|
|
46
46
|
duplicates: 0,
|
|
47
47
|
processed: issues.files.size,
|
|
48
|
+
total: projectFiles.length,
|
|
48
49
|
};
|
|
49
|
-
const
|
|
50
|
-
if (!isShowProgress)
|
|
51
|
-
return;
|
|
52
|
-
const counter = counters.processed;
|
|
53
|
-
const total = projectFiles.length;
|
|
54
|
-
const percentage = Math.floor((counter / total) * 100);
|
|
55
|
-
const messages = [(0, log_1.getLine)(`${percentage}%`, `of files processed (${counter} of ${total})`)];
|
|
56
|
-
report.files && messages.push((0, log_1.getLine)(unreferencedProductionFiles.length, 'unused files'));
|
|
57
|
-
report.unlisted && messages.push((0, log_1.getLine)(counters.unresolved, 'unlisted dependencies'));
|
|
58
|
-
report.exports && messages.push((0, log_1.getLine)(counters.exports, 'unused exports'));
|
|
59
|
-
report.nsExports && messages.push((0, log_1.getLine)(counters.nsExports, 'unused exports in namespace'));
|
|
60
|
-
report.types && messages.push((0, log_1.getLine)(counters.types, 'unused types'));
|
|
61
|
-
report.nsTypes && messages.push((0, log_1.getLine)(counters.nsTypes, 'unused types in namespace'));
|
|
62
|
-
report.duplicates && messages.push((0, log_1.getLine)(counters.duplicates, 'duplicate exports'));
|
|
63
|
-
if (counter < total) {
|
|
64
|
-
messages.push('');
|
|
65
|
-
messages.push(`Processing: ${node_path_1.default.relative(workingDir, item.filePath)}`);
|
|
66
|
-
}
|
|
67
|
-
lineRewriter.update(messages);
|
|
68
|
-
};
|
|
50
|
+
const updateCounters = (0, progress_1.getCountersUpdater)(configuration, counters);
|
|
69
51
|
const addSymbolIssue = (issueType, issue) => {
|
|
70
52
|
const { filePath, symbol } = issue;
|
|
71
53
|
const key = node_path_1.default.relative(workingDir, filePath);
|
|
72
54
|
issues[issueType][key] = issues[issueType][key] ?? {};
|
|
73
55
|
issues[issueType][key][symbol] = issue;
|
|
74
56
|
counters[issueType]++;
|
|
75
|
-
|
|
57
|
+
updateCounters(issue);
|
|
76
58
|
};
|
|
77
59
|
const addProjectIssue = (issueType, issue) => {
|
|
78
60
|
if (!issues[issueType].has(issue.symbol)) {
|
|
79
61
|
issues[issueType].add(issue.symbol);
|
|
80
62
|
counters[issueType]++;
|
|
81
63
|
}
|
|
82
|
-
|
|
64
|
+
updateCounters(issue);
|
|
83
65
|
};
|
|
66
|
+
updateMessage('Connecting the dots...');
|
|
84
67
|
if (report.dependencies ||
|
|
85
68
|
report.unlisted ||
|
|
86
69
|
report.exports ||
|
|
@@ -197,8 +180,7 @@ async function findIssues(configuration) {
|
|
|
197
180
|
unusedDevDependencies.forEach(symbol => addProjectIssue('devDependencies', { filePath: '', symbol }));
|
|
198
181
|
}
|
|
199
182
|
}
|
|
200
|
-
|
|
201
|
-
lineRewriter.resetLines();
|
|
183
|
+
updateCounters();
|
|
202
184
|
return { issues, counters };
|
|
203
185
|
}
|
|
204
186
|
exports.findIssues = findIssues;
|
package/dist/types.d.ts
CHANGED
package/dist/util/fs.d.ts
CHANGED
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.
|
|
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",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"knip": "dist/cli.js"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
-
"knip": "node ./dist/cli.js",
|
|
27
|
+
"knip": "node ./dist/cli.js --include files,dependencies,unlisted",
|
|
28
28
|
"test": "node --loader tsx --test test/*.spec.ts",
|
|
29
29
|
"watch": "tsc --watch",
|
|
30
30
|
"build": "rm -rf dist && tsc",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/micromatch": "4.0.2",
|
|
52
|
-
"@types/node": "18.
|
|
52
|
+
"@types/node": "18.11.2",
|
|
53
53
|
"prettier": "2.7.1",
|
|
54
54
|
"release-it": "15.5.0",
|
|
55
|
-
"tsx": "3.10.
|
|
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
|
}
|