knip 0.8.0 → 0.8.2
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 +7 -8
- 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/symbols.js +9 -6
- package/dist/runner.d.ts +2 -13
- package/dist/runner.js +21 -34
- package/dist/types.d.ts +1 -0
- package/package.json +3 -3
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 {
|
|
@@ -60,13 +60,12 @@ const run = async () => {
|
|
|
60
60
|
},
|
|
61
61
|
});
|
|
62
62
|
printReport({ report, issues, cwd, workingDir, isDev, options: reporterOptions });
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
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;
|
|
@@ -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,19 +10,19 @@ 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);
|
|
22
|
-
(0, debug_1.debugLogSourceFiles)(configuration, 1, '
|
|
23
|
-
(0, debug_1.debugLogSourceFiles)(configuration, 1, '
|
|
24
|
-
(0, debug_1.debugLogSourceFiles)(configuration, 1, '
|
|
25
|
-
(0, debug_1.debugLogSourceFiles)(configuration, 1, '
|
|
22
|
+
(0, debug_1.debugLogSourceFiles)(configuration, 1, 'Used production files', usedProductionFiles);
|
|
23
|
+
(0, debug_1.debugLogSourceFiles)(configuration, 1, 'Unreferenced production files', unreferencedProductionFiles);
|
|
24
|
+
(0, debug_1.debugLogSourceFiles)(configuration, 1, 'Used entry files', usedEntryFiles);
|
|
25
|
+
(0, debug_1.debugLogSourceFiles)(configuration, 1, 'Used non-entry files', usedNonEntryFiles);
|
|
26
26
|
const issues = {
|
|
27
27
|
files: new Set(unreferencedProductionFiles.map(file => file.getFilePath())),
|
|
28
28
|
dependencies: new Set(),
|
|
@@ -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 ||
|
|
@@ -103,7 +86,7 @@ async function findIssues(configuration) {
|
|
|
103
86
|
addSymbolIssue('duplicates', { filePath, symbol, symbols });
|
|
104
87
|
});
|
|
105
88
|
}
|
|
106
|
-
if (!isIncludeEntryFiles &&
|
|
89
|
+
if (!isIncludeEntryFiles && usedEntryFiles.includes(sourceFile))
|
|
107
90
|
return;
|
|
108
91
|
if (report.exports || report.types || report.nsExports || report.nsTypes) {
|
|
109
92
|
if (!isIncludeEntryFiles) {
|
|
@@ -128,8 +111,13 @@ async function findIssues(configuration) {
|
|
|
128
111
|
identifier = declaration;
|
|
129
112
|
}
|
|
130
113
|
else if (declaration.isKind(ts_morph_1.ts.SyntaxKind.ArrowFunction) ||
|
|
131
|
-
declaration.isKind(ts_morph_1.ts.SyntaxKind.ObjectLiteralExpression)
|
|
132
|
-
|
|
114
|
+
declaration.isKind(ts_morph_1.ts.SyntaxKind.ObjectLiteralExpression) ||
|
|
115
|
+
declaration.isKind(ts_morph_1.ts.SyntaxKind.ArrayLiteralExpression) ||
|
|
116
|
+
declaration.isKind(ts_morph_1.ts.SyntaxKind.StringLiteral) ||
|
|
117
|
+
declaration.isKind(ts_morph_1.ts.SyntaxKind.NumericLiteral)) {
|
|
118
|
+
if (!(0, ts_morph_helpers_1.hasReferencingDefaultImport)(sourceFile)) {
|
|
119
|
+
fakeIdentifier = 'default';
|
|
120
|
+
}
|
|
133
121
|
}
|
|
134
122
|
else if (declaration.isKind(ts_morph_1.ts.SyntaxKind.FunctionDeclaration) ||
|
|
135
123
|
declaration.isKind(ts_morph_1.ts.SyntaxKind.ClassDeclaration) ||
|
|
@@ -192,8 +180,7 @@ async function findIssues(configuration) {
|
|
|
192
180
|
unusedDevDependencies.forEach(symbol => addProjectIssue('devDependencies', { filePath: '', symbol }));
|
|
193
181
|
}
|
|
194
182
|
}
|
|
195
|
-
|
|
196
|
-
lineRewriter.resetLines();
|
|
183
|
+
updateCounters();
|
|
197
184
|
return { issues, counters };
|
|
198
185
|
}
|
|
199
186
|
exports.findIssues = findIssues;
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knip",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
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",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"is-builtin-module": "3.2.0",
|
|
46
46
|
"micromatch": "4.0.5",
|
|
47
47
|
"ts-morph": "16.0.0",
|
|
48
|
-
"ts-morph-helpers": "0.
|
|
48
|
+
"ts-morph-helpers": "0.6.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/micromatch": "4.0.2",
|