knip 0.9.0 → 0.10.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 +7 -5
- package/dist/cli.js +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +18 -19
- package/dist/progress.js +4 -7
- package/dist/reporters/codeowners.d.ts +1 -1
- package/dist/reporters/codeowners.js +31 -59
- package/dist/reporters/compact.d.ts +1 -1
- package/dist/reporters/compact.js +27 -63
- package/dist/reporters/constants.d.ts +11 -0
- package/dist/reporters/constants.js +14 -0
- package/dist/reporters/index.d.ts +4 -4
- package/dist/reporters/json.d.ts +1 -1
- package/dist/reporters/json.js +20 -44
- package/dist/reporters/symbols.d.ts +1 -1
- package/dist/reporters/symbols.js +21 -46
- package/dist/runner.js +9 -9
- package/dist/types.d.ts +16 -19
- package/dist/util/config.d.ts +2 -2
- package/dist/util/config.js +14 -23
- package/dist/util/debug.d.ts +6 -9
- package/dist/util/debug.js +9 -12
- package/dist/util/dependencies.js +21 -15
- package/dist/util/path.d.ts +1 -0
- package/dist/util/path.js +8 -5
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -125,10 +125,12 @@ following types of issues:
|
|
|
125
125
|
- `nsTypes` - Unused types in namespaces: did not find direct references to this exported variable (2)
|
|
126
126
|
- `duplicates` - Duplicate exports: the same thing is exported more than once with different names from the same file
|
|
127
127
|
|
|
128
|
-
1. This
|
|
129
|
-
|
|
128
|
+
1. This includes dependencies that could not be resolved. For instance, what does `unresolved/dir/module` mean?
|
|
129
|
+
- To target something in the (missing) `node_modules/unresolved` package?
|
|
130
|
+
- Target a local module that should have a relative path?
|
|
131
|
+
- It does not match any `paths` entry in `tsconfig.json#compilerOptions`.
|
|
130
132
|
2. The variable or type is not referenced directly, and has become a member of a namespace. That's why Knip is not sure
|
|
131
|
-
whether this export can be removed, so please look into it
|
|
133
|
+
whether this export can be removed, so please look into it.
|
|
132
134
|
|
|
133
135
|
You can `--include` or `--exclude` any of the types to slice & dice the report to your needs. Alternatively, they can be
|
|
134
136
|
added to the configuration (e.g. `"exclude": ["dependencies"]`).
|
|
@@ -140,8 +142,8 @@ As always, make sure to backup files or use Git before deleting files or making
|
|
|
140
142
|
- Unused files can be removed.
|
|
141
143
|
- Unused dependencies can be removed from `package.json`.
|
|
142
144
|
- Unlisted dependencies should be added to `package.json`.
|
|
143
|
-
- Unused exports and types: remove the `export` keyword in front of unused exports. Then you (or tools such as
|
|
144
|
-
TypeScript language
|
|
145
|
+
- Unused exports and types: remove the `export` keyword in front of unused exports. Then you (or tools such as
|
|
146
|
+
TypeScript language services in VS Code and/or ESLint) can see whether the variable or type is used within the same
|
|
145
147
|
file. If this is not the case, it can be removed.
|
|
146
148
|
|
|
147
149
|
🔁 Repeat the process to reveal new unused files and exports. Sometimes it's so liberating to remove things!
|
package/dist/cli.js
CHANGED
|
@@ -62,7 +62,6 @@ const run = async () => {
|
|
|
62
62
|
await printReport({ report, issues, cwd, workingDir, isDev, options: reporterOptions });
|
|
63
63
|
const totalErrorCount = Object.keys(report)
|
|
64
64
|
.filter(reportGroup => report[reportGroup])
|
|
65
|
-
.map(reportGroup => (reportGroup === 'unlisted' ? 'unresolved' : reportGroup))
|
|
66
65
|
.reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
|
|
67
66
|
if (totalErrorCount > Number(maxIssues))
|
|
68
67
|
process.exit(totalErrorCount);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { UnresolvedConfiguration } from './types';
|
|
2
|
-
export declare const main: (
|
|
2
|
+
export declare const main: (unresolvedConfiguration: UnresolvedConfiguration) => Promise<{
|
|
3
3
|
report: import("./types").Report;
|
|
4
4
|
issues: import("./types").Issues;
|
|
5
5
|
counters: import("./types").Counters;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.main = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
7
|
const typescript_1 = __importDefault(require("typescript"));
|
|
9
8
|
const config_1 = require("./util/config");
|
|
10
9
|
const fs_1 = require("./util/fs");
|
|
@@ -14,10 +13,10 @@ const runner_1 = require("./runner");
|
|
|
14
13
|
const errors_1 = require("./util/errors");
|
|
15
14
|
const debug_1 = require("./util/debug");
|
|
16
15
|
const progress_1 = require("./progress");
|
|
17
|
-
const main = async (
|
|
18
|
-
const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } =
|
|
19
|
-
const updateMessage = (0, progress_1.getMessageUpdater)(
|
|
20
|
-
(0, debug_1.debugLogObject)(
|
|
16
|
+
const main = async (unresolvedConfiguration) => {
|
|
17
|
+
const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } = unresolvedConfiguration;
|
|
18
|
+
const updateMessage = (0, progress_1.getMessageUpdater)(unresolvedConfiguration);
|
|
19
|
+
(0, debug_1.debugLogObject)(debug, 1, 'Unresolved configuration', unresolvedConfiguration);
|
|
21
20
|
updateMessage('Reading configuration and manifest files...');
|
|
22
21
|
const manifestPath = await (0, fs_1.findFile)(cwd, workingDir, 'package.json');
|
|
23
22
|
const manifest = manifestPath && require(manifestPath);
|
|
@@ -32,19 +31,19 @@ const main = async (options) => {
|
|
|
32
31
|
if (tsConfigFilePathArg && !resolvedTsConfigFilePath) {
|
|
33
32
|
throw new errors_1.ConfigurationError(`Unable to find ${tsConfigFilePathArg}`);
|
|
34
33
|
}
|
|
35
|
-
let
|
|
34
|
+
let tsConfigPathGlobs = [];
|
|
36
35
|
if (resolvedTsConfigFilePath) {
|
|
37
36
|
const config = typescript_1.default.readConfigFile(resolvedTsConfigFilePath, typescript_1.default.sys.readFile);
|
|
38
|
-
|
|
37
|
+
tsConfigPathGlobs = config.config.compilerOptions?.paths
|
|
39
38
|
? Object.keys(config.config.compilerOptions.paths).map(p => p.replace(/\*/g, '**'))
|
|
40
39
|
: [];
|
|
41
40
|
if (config.error) {
|
|
42
|
-
throw new errors_1.ConfigurationError(`Unable to read ${
|
|
41
|
+
throw new errors_1.ConfigurationError(`Unable to read ${(0, path_1.relative)(resolvedTsConfigFilePath)}`);
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
|
-
const dir =
|
|
44
|
+
const dir = (0, path_1.relative)(workingDir);
|
|
46
45
|
const resolvedConfig = (0, config_1.resolveConfig)(manifest.knip ?? localConfig, { workingDir: dir, isDev });
|
|
47
|
-
(0, debug_1.debugLogObject)(
|
|
46
|
+
(0, debug_1.debugLogObject)(debug, 1, 'Resolved configuration', resolvedConfig);
|
|
48
47
|
if (!resolvedConfigFilePath && !manifest.knip && !resolvedTsConfigFilePath) {
|
|
49
48
|
throw new errors_1.ConfigurationError(`Unable to find ${configFilePath} or package.json#knip or ${tsConfigFilePath}`);
|
|
50
49
|
}
|
|
@@ -62,13 +61,13 @@ const main = async (options) => {
|
|
|
62
61
|
ignore,
|
|
63
62
|
gitignore,
|
|
64
63
|
});
|
|
65
|
-
(0, debug_1.debugLogFiles)(
|
|
64
|
+
(0, debug_1.debugLogFiles)(debug, 1, 'Globbed entry paths', entryPaths);
|
|
66
65
|
const production = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, entryPaths);
|
|
67
66
|
const entryFiles = production.getSourceFiles();
|
|
68
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
67
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Included entry source files', entryFiles);
|
|
69
68
|
production.resolveSourceFileDependencies();
|
|
70
69
|
const productionFiles = production.getSourceFiles();
|
|
71
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
70
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Included production source files', productionFiles);
|
|
72
71
|
updateMessage('Resolving project files...');
|
|
73
72
|
const projectPaths = await (0, path_1.resolvePaths)({
|
|
74
73
|
cwd,
|
|
@@ -77,10 +76,10 @@ const main = async (options) => {
|
|
|
77
76
|
ignore,
|
|
78
77
|
gitignore,
|
|
79
78
|
});
|
|
80
|
-
(0, debug_1.debugLogFiles)(
|
|
79
|
+
(0, debug_1.debugLogFiles)(debug, 1, 'Globbed project paths', projectPaths);
|
|
81
80
|
const project = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, projectPaths);
|
|
82
81
|
const projectFiles = project.getSourceFiles();
|
|
83
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
82
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Included project source files', projectFiles);
|
|
84
83
|
return { entryFiles, productionFiles, projectFiles };
|
|
85
84
|
}
|
|
86
85
|
else {
|
|
@@ -90,7 +89,7 @@ const main = async (options) => {
|
|
|
90
89
|
return { entryFiles: files, productionFiles: files, projectFiles: files };
|
|
91
90
|
}
|
|
92
91
|
})();
|
|
93
|
-
const report = (0, config_1.
|
|
92
|
+
const report = (0, config_1.resolveIncludedIssueTypes)(include, resolvedConfig ? exclude : ['files'], resolvedConfig);
|
|
94
93
|
const config = {
|
|
95
94
|
workingDir,
|
|
96
95
|
report,
|
|
@@ -102,8 +101,8 @@ const main = async (options) => {
|
|
|
102
101
|
peerDependencies: Object.keys(manifest.peerDependencies ?? {}),
|
|
103
102
|
optionalDependencies: Object.keys(manifest.optionalDependencies ?? {}),
|
|
104
103
|
devDependencies: Object.keys(manifest.devDependencies ?? {}),
|
|
105
|
-
isDev:
|
|
106
|
-
|
|
104
|
+
isDev: Boolean(resolvedConfig?.dev),
|
|
105
|
+
tsConfigPathGlobs: tsConfigPathGlobs,
|
|
107
106
|
isShowProgress,
|
|
108
107
|
jsDocOptions: {
|
|
109
108
|
isReadPublicTag: jsDoc.includes('public'),
|
|
@@ -111,7 +110,7 @@ const main = async (options) => {
|
|
|
111
110
|
debug,
|
|
112
111
|
};
|
|
113
112
|
const { issues, counters } = await (0, runner_1.findIssues)(config);
|
|
114
|
-
(0, debug_1.debugLogObject)(
|
|
113
|
+
(0, debug_1.debugLogObject)(debug, 2, 'Issues', issues);
|
|
115
114
|
return { report, issues, counters };
|
|
116
115
|
};
|
|
117
116
|
exports.main = main;
|
package/dist/progress.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.getCountersUpdater = exports.getMessageUpdater = void 0;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
4
|
const log_1 = require("./log");
|
|
5
|
+
const path_1 = require("./util/path");
|
|
9
6
|
const lineRewriter = new log_1.LineRewriter();
|
|
10
7
|
const getMessageUpdater = (configuration) => {
|
|
11
8
|
const { isShowProgress } = configuration;
|
|
@@ -15,7 +12,7 @@ const getMessageUpdater = (configuration) => {
|
|
|
15
12
|
};
|
|
16
13
|
exports.getMessageUpdater = getMessageUpdater;
|
|
17
14
|
const getCountersUpdater = (configuration, counters) => {
|
|
18
|
-
const {
|
|
15
|
+
const { isShowProgress, report } = configuration;
|
|
19
16
|
if (!isShowProgress)
|
|
20
17
|
return () => { };
|
|
21
18
|
return (issue) => {
|
|
@@ -25,7 +22,7 @@ const getCountersUpdater = (configuration, counters) => {
|
|
|
25
22
|
const percentage = Math.floor((processed / total) * 100);
|
|
26
23
|
const messages = [(0, log_1.getLine)(`${percentage}%`, `of files processed (${processed} of ${total})`)];
|
|
27
24
|
report.files && messages.push((0, log_1.getLine)(counters.files, 'unused files'));
|
|
28
|
-
report.unlisted && messages.push((0, log_1.getLine)(counters.
|
|
25
|
+
report.unlisted && messages.push((0, log_1.getLine)(counters.unlisted, 'unlisted dependencies'));
|
|
29
26
|
report.exports && messages.push((0, log_1.getLine)(counters.exports, 'unused exports'));
|
|
30
27
|
report.nsExports && messages.push((0, log_1.getLine)(counters.nsExports, 'unused exports in namespace'));
|
|
31
28
|
report.types && messages.push((0, log_1.getLine)(counters.types, 'unused types'));
|
|
@@ -33,7 +30,7 @@ const getCountersUpdater = (configuration, counters) => {
|
|
|
33
30
|
report.duplicates && messages.push((0, log_1.getLine)(counters.duplicates, 'duplicate exports'));
|
|
34
31
|
if (processed < total) {
|
|
35
32
|
messages.push('');
|
|
36
|
-
messages.push(`Processing: ${
|
|
33
|
+
messages.push(`Processing: ${(0, path_1.relative)(issue.filePath)}`);
|
|
37
34
|
}
|
|
38
35
|
lineRewriter.update(messages);
|
|
39
36
|
};
|
|
@@ -5,31 +5,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const node_path_1 = __importDefault(require("node:path"));
|
|
7
7
|
const ownership_1 = require("@snyk/github-codeowners/dist/lib/ownership");
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const path_1 = require("../util/path");
|
|
9
|
+
const constants_1 = require("./constants");
|
|
10
|
+
const logIssueLine = (owner, filePath, symbols) => {
|
|
11
|
+
console.log(`${owner} ${(0, path_1.relative)(filePath)}${symbols ? `: ${symbols.join(', ')}` : ''}`);
|
|
10
12
|
};
|
|
11
|
-
const
|
|
13
|
+
const logIssueSet = (issues, title) => {
|
|
12
14
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
13
15
|
if (issues.length) {
|
|
14
16
|
issues
|
|
15
17
|
.sort((a, b) => (a.owner < b.owner ? -1 : 1))
|
|
16
|
-
.forEach(issue => console.log(issue.owner, issue.symbol.startsWith('/') ?
|
|
18
|
+
.forEach(issue => console.log(issue.owner, issue.symbol.startsWith('/') ? (0, path_1.relative)(issue.symbol) : issue.symbol));
|
|
17
19
|
}
|
|
18
20
|
else {
|
|
19
21
|
console.log('Not found');
|
|
20
22
|
}
|
|
21
23
|
};
|
|
22
|
-
const
|
|
24
|
+
const logIssueRecord = (issues, title) => {
|
|
23
25
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
24
26
|
if (issues.length) {
|
|
25
27
|
const sortedByFilePath = issues.sort((a, b) => (a.owner < b.owner ? -1 : 1));
|
|
26
|
-
sortedByFilePath.forEach(({ filePath, symbols, owner }) => logIssueLine(owner,
|
|
28
|
+
sortedByFilePath.forEach(({ filePath, symbols, owner }) => logIssueLine(owner, filePath, symbols));
|
|
27
29
|
}
|
|
28
30
|
else {
|
|
29
31
|
console.log('Not found');
|
|
30
32
|
}
|
|
31
33
|
};
|
|
32
|
-
exports.default = ({ report, issues,
|
|
34
|
+
exports.default = ({ report, issues, options }) => {
|
|
33
35
|
let opts = {};
|
|
34
36
|
try {
|
|
35
37
|
opts = options ? JSON.parse(options) : opts;
|
|
@@ -42,57 +44,27 @@ exports.default = ({ report, issues, cwd, isDev, options }) => {
|
|
|
42
44
|
const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
|
|
43
45
|
const [dependenciesOwner = '[no-owner]'] = codeownersEngine.calcFileOwnership('package.json');
|
|
44
46
|
const fallbackOwner = dependenciesOwner;
|
|
45
|
-
const calcFileOwnership = (filePath) => codeownersEngine.calcFileOwnership(
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
owner: dependenciesOwner,
|
|
68
|
-
}));
|
|
69
|
-
logIssueGroupResult(unreferencedDevDependencies, cwd, 'UNUSED DEV DEPENDENCIES');
|
|
70
|
-
}
|
|
71
|
-
if (report.unlisted) {
|
|
72
|
-
const unreferencedDependencies = Object.values(issues.unresolved).map(toIssueWithOwner);
|
|
73
|
-
logIssueGroupResults(unreferencedDependencies, cwd, reportMultipleGroups && 'UNLISTED DEPENDENCIES');
|
|
74
|
-
}
|
|
75
|
-
if (report.exports) {
|
|
76
|
-
const unreferencedExports = Object.values(issues.exports).map(toIssueWithOwner);
|
|
77
|
-
logIssueGroupResults(unreferencedExports, cwd, reportMultipleGroups && 'UNUSED EXPORTS');
|
|
78
|
-
}
|
|
79
|
-
if (report.nsExports) {
|
|
80
|
-
const unreferencedNsExports = Object.values(issues.nsExports).map(toIssueWithOwner);
|
|
81
|
-
logIssueGroupResults(unreferencedNsExports, cwd, reportMultipleGroups && 'UNUSED EXPORTS IN NAMESPACE');
|
|
82
|
-
}
|
|
83
|
-
if (report.types) {
|
|
84
|
-
const unreferencedTypes = Object.values(issues.types).map(toIssueWithOwner);
|
|
85
|
-
logIssueGroupResults(unreferencedTypes, cwd, reportMultipleGroups && 'UNUSED TYPES');
|
|
86
|
-
}
|
|
87
|
-
if (report.nsTypes) {
|
|
88
|
-
const unreferencedNsTypes = Object.values(issues.nsTypes).map(toIssueWithOwner);
|
|
89
|
-
logIssueGroupResults(unreferencedNsTypes, cwd, reportMultipleGroups && 'UNUSED TYPES IN NAMESPACE');
|
|
90
|
-
}
|
|
91
|
-
if (report.duplicates) {
|
|
92
|
-
const unreferencedDuplicates = Object.values(issues.duplicates)
|
|
93
|
-
.map(issues => Object.values(issues))
|
|
94
|
-
.flat()
|
|
95
|
-
.map(issue => ({ ...issue, owner: calcFileOwnership(issue.filePath) }));
|
|
96
|
-
logIssueGroupResults(unreferencedDuplicates, cwd, reportMultipleGroups && 'DUPLICATE EXPORTS');
|
|
47
|
+
const calcFileOwnership = (filePath) => codeownersEngine.calcFileOwnership((0, path_1.relative)(filePath))[0] ?? fallbackOwner;
|
|
48
|
+
const addOwner = (issue) => ({ ...issue, owner: calcFileOwnership(issue.filePath) });
|
|
49
|
+
for (const [reportType, isReportType] of Object.entries(report)) {
|
|
50
|
+
if (isReportType) {
|
|
51
|
+
const title = reportMultipleGroups && constants_1.ISSUE_TYPE_TITLE[reportType];
|
|
52
|
+
if (issues[reportType] instanceof Set) {
|
|
53
|
+
const toIssue = (filePath) => ({ filePath, symbol: filePath });
|
|
54
|
+
const issuesForType = Array.from(issues[reportType]).map(toIssue);
|
|
55
|
+
logIssueSet(issuesForType.map(addOwner), title);
|
|
56
|
+
}
|
|
57
|
+
else if (reportType === 'duplicates') {
|
|
58
|
+
const issuesForType = Object.values(issues[reportType]).map(Object.values).flat().map(addOwner);
|
|
59
|
+
logIssueRecord(issuesForType, title);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const issuesForType = Object.values(issues[reportType]).map(issues => {
|
|
63
|
+
const items = Object.values(issues);
|
|
64
|
+
return addOwner({ ...items[0], symbols: items.map(issue => issue.symbol) });
|
|
65
|
+
});
|
|
66
|
+
logIssueRecord(issuesForType, title);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
97
69
|
}
|
|
98
70
|
};
|
|
@@ -1,84 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
3
|
+
const path_1 = require("../util/path");
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const logIssueLine = (filePath, symbols) => {
|
|
6
|
+
console.log(`${(0, path_1.relative)(filePath)}${symbols ? `: ${symbols.join(', ')}` : ''}`);
|
|
9
7
|
};
|
|
10
|
-
const
|
|
8
|
+
const logIssueSet = (issues, title) => {
|
|
11
9
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
12
10
|
if (issues.length) {
|
|
13
|
-
issues.sort().forEach(value => console.log(value.startsWith('/') ?
|
|
11
|
+
issues.sort().forEach(value => console.log(value.startsWith('/') ? (0, path_1.relative)(value) : value));
|
|
14
12
|
}
|
|
15
13
|
else {
|
|
16
14
|
console.log('Not found');
|
|
17
15
|
}
|
|
18
16
|
};
|
|
19
|
-
const
|
|
17
|
+
const logIssueRecord = (issues, title) => {
|
|
20
18
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
21
19
|
if (issues.length) {
|
|
22
20
|
const sortedByFilePath = issues.sort((a, b) => (a.filePath > b.filePath ? 1 : -1));
|
|
23
|
-
sortedByFilePath.forEach(({ filePath, symbols }) => logIssueLine(
|
|
21
|
+
sortedByFilePath.forEach(({ filePath, symbols }) => logIssueLine(filePath, symbols));
|
|
24
22
|
}
|
|
25
23
|
else {
|
|
26
24
|
console.log('Not found');
|
|
27
25
|
}
|
|
28
26
|
};
|
|
29
|
-
exports.default = ({ report, issues
|
|
27
|
+
exports.default = ({ report, issues }) => {
|
|
30
28
|
const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
if (report.exports) {
|
|
51
|
-
const unreferencedExports = Object.values(issues.exports).map(issues => {
|
|
52
|
-
const items = Object.values(issues);
|
|
53
|
-
return { ...items[0], symbols: items.map(i => i.symbol) };
|
|
54
|
-
});
|
|
55
|
-
logIssueGroupResults(unreferencedExports, workingDir, reportMultipleGroups && 'UNUSED EXPORTS');
|
|
56
|
-
}
|
|
57
|
-
if (report.nsExports) {
|
|
58
|
-
const unreferencedNsExports = Object.values(issues.nsExports).map(issues => {
|
|
59
|
-
const items = Object.values(issues);
|
|
60
|
-
return { ...items[0], symbols: items.map(i => i.symbol) };
|
|
61
|
-
});
|
|
62
|
-
logIssueGroupResults(unreferencedNsExports, workingDir, reportMultipleGroups && 'UNUSED EXPORTS IN NAMESPACE');
|
|
63
|
-
}
|
|
64
|
-
if (report.types) {
|
|
65
|
-
const unreferencedTypes = Object.values(issues.types).map(issues => {
|
|
66
|
-
const items = Object.values(issues);
|
|
67
|
-
return { ...items[0], symbols: items.map(i => i.symbol) };
|
|
68
|
-
});
|
|
69
|
-
logIssueGroupResults(unreferencedTypes, workingDir, reportMultipleGroups && 'UNUSED TYPES');
|
|
70
|
-
}
|
|
71
|
-
if (report.nsTypes) {
|
|
72
|
-
const unreferencedNsTypes = Object.values(issues.nsTypes).map(issues => {
|
|
73
|
-
const items = Object.values(issues);
|
|
74
|
-
return { ...items[0], symbols: items.map(i => i.symbol) };
|
|
75
|
-
});
|
|
76
|
-
logIssueGroupResults(unreferencedNsTypes, workingDir, reportMultipleGroups && 'UNUSED TYPES IN NAMESPACE');
|
|
77
|
-
}
|
|
78
|
-
if (report.duplicates) {
|
|
79
|
-
const unreferencedDuplicates = Object.values(issues.duplicates)
|
|
80
|
-
.map(issues => Object.values(issues))
|
|
81
|
-
.flat();
|
|
82
|
-
logIssueGroupResults(unreferencedDuplicates, workingDir, reportMultipleGroups && 'DUPLICATE EXPORTS');
|
|
29
|
+
for (const [reportType, isReportType] of Object.entries(report)) {
|
|
30
|
+
if (isReportType) {
|
|
31
|
+
const title = reportMultipleGroups && constants_1.ISSUE_TYPE_TITLE[reportType];
|
|
32
|
+
if (issues[reportType] instanceof Set) {
|
|
33
|
+
logIssueSet(Array.from(issues[reportType]), title);
|
|
34
|
+
}
|
|
35
|
+
else if (reportType === 'duplicates') {
|
|
36
|
+
const issuesForType = Object.values(issues[reportType]).map(Object.values).flat();
|
|
37
|
+
logIssueRecord(issuesForType, title);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const issuesForType = Object.values(issues[reportType]).map(issues => {
|
|
41
|
+
const items = Object.values(issues);
|
|
42
|
+
return { ...items[0], symbols: items.map(issue => issue.symbol) };
|
|
43
|
+
});
|
|
44
|
+
logIssueRecord(issuesForType, title);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
83
47
|
}
|
|
84
48
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ISSUE_TYPE_TITLE = void 0;
|
|
4
|
+
exports.ISSUE_TYPE_TITLE = {
|
|
5
|
+
files: 'UNUSED FILES',
|
|
6
|
+
dependencies: 'UNUSED DEPENDENCIES',
|
|
7
|
+
devDependencies: 'UNUSED DEV DEPENDENCIES',
|
|
8
|
+
unlisted: 'UNLISTED DEPENDENCIES',
|
|
9
|
+
exports: 'UNUSED EXPORTS',
|
|
10
|
+
nsExports: 'UNUSED EXPORTS IN NAMESPACE',
|
|
11
|
+
types: 'UNUSED TYPES',
|
|
12
|
+
nsTypes: 'UNUSED TYPES IN NAMESPACE',
|
|
13
|
+
duplicates: 'DUPLICATE EXPORTS',
|
|
14
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
declare const _default: {
|
|
2
|
-
symbols: ({ report, issues
|
|
3
|
-
compact: ({ report, issues
|
|
4
|
-
codeowners: ({ report, issues,
|
|
5
|
-
json: ({ report, issues,
|
|
2
|
+
symbols: ({ report, issues }: import("../types").ReporterOptions) => void;
|
|
3
|
+
compact: ({ report, issues }: import("../types").ReporterOptions) => void;
|
|
4
|
+
codeowners: ({ report, issues, options }: import("../types").ReporterOptions) => void;
|
|
5
|
+
json: ({ report, issues, options }: import("../types").ReporterOptions) => Promise<void>;
|
|
6
6
|
};
|
|
7
7
|
export default _default;
|
package/dist/reporters/json.d.ts
CHANGED
package/dist/reporters/json.js
CHANGED
|
@@ -5,8 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const node_path_1 = __importDefault(require("node:path"));
|
|
7
7
|
const fs_1 = require("../util/fs");
|
|
8
|
+
const path_1 = require("../util/path");
|
|
8
9
|
const ownership_1 = require("@snyk/github-codeowners/dist/lib/ownership");
|
|
9
|
-
|
|
10
|
+
const mergeTypes = (type) => type === 'exports' || type === 'nsExports' ? 'exports' : type === 'types' || type === 'nsTypes' ? 'types' : type;
|
|
11
|
+
exports.default = async ({ report, issues, options }) => {
|
|
10
12
|
let opts = {};
|
|
11
13
|
try {
|
|
12
14
|
opts = options ? JSON.parse(options) : opts;
|
|
@@ -19,7 +21,7 @@ exports.default = async ({ report, issues, cwd, options }) => {
|
|
|
19
21
|
const codeownersEngine = (await (0, fs_1.isFile)(codeownersFilePath)) && ownership_1.OwnershipEngine.FromCodeownersFile(codeownersFilePath);
|
|
20
22
|
const flatten = (issues) => Object.values(issues).map(Object.values).flat();
|
|
21
23
|
const initRow = (filePath) => {
|
|
22
|
-
const file =
|
|
24
|
+
const file = (0, path_1.relative)(filePath);
|
|
23
25
|
const row = {
|
|
24
26
|
file,
|
|
25
27
|
...(codeownersEngine && { owners: codeownersEngine.calcFileOwnership(file) }),
|
|
@@ -31,48 +33,22 @@ exports.default = async ({ report, issues, cwd, options }) => {
|
|
|
31
33
|
};
|
|
32
34
|
return row;
|
|
33
35
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
});
|
|
36
|
+
for (const [reportType, isReportType] of Object.entries(report)) {
|
|
37
|
+
if (isReportType) {
|
|
38
|
+
if (reportType === 'files') {
|
|
39
|
+
Array.from(issues[reportType]).forEach(filePath => {
|
|
40
|
+
json[filePath] = json[filePath] ?? initRow(filePath);
|
|
41
|
+
json[filePath][reportType] = true;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const type = mergeTypes(reportType);
|
|
46
|
+
flatten(issues[reportType]).forEach(({ filePath, symbol }) => {
|
|
47
|
+
json[filePath] = json[filePath] ?? initRow(filePath);
|
|
48
|
+
json[filePath][type]?.push(symbol);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
76
52
|
}
|
|
77
53
|
console.log(JSON.stringify(Object.values(json)));
|
|
78
54
|
};
|
|
@@ -1,72 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
3
|
+
const path_1 = require("../util/path");
|
|
4
|
+
const constants_1 = require("./constants");
|
|
7
5
|
const TRUNCATE_WIDTH = 40;
|
|
8
|
-
const logIssueLine = (issue,
|
|
6
|
+
const logIssueLine = (issue, maxWidth) => {
|
|
9
7
|
const symbols = issue.symbols ? issue.symbols.join(', ') : issue.symbol;
|
|
10
8
|
const truncatedSymbol = symbols.length > maxWidth ? symbols.slice(0, maxWidth - 3) + '...' : symbols;
|
|
11
|
-
const filePath =
|
|
9
|
+
const filePath = (0, path_1.relative)(issue.filePath);
|
|
12
10
|
console.log(`${truncatedSymbol.padEnd(maxWidth + 2)}${issue.symbolType?.padEnd(11) || ''}${filePath}`);
|
|
13
11
|
};
|
|
14
|
-
const
|
|
12
|
+
const logIssueSet = (issues, title) => {
|
|
15
13
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
16
14
|
if (issues.length) {
|
|
17
|
-
issues.sort().forEach(value => console.log(value.startsWith('/') ?
|
|
15
|
+
issues.sort().forEach(value => console.log(value.startsWith('/') ? (0, path_1.relative)(value) : value));
|
|
18
16
|
}
|
|
19
17
|
else {
|
|
20
18
|
console.log('Not found');
|
|
21
19
|
}
|
|
22
20
|
};
|
|
23
|
-
const
|
|
21
|
+
const logIssueRecord = (issues, title, isTruncate = false) => {
|
|
24
22
|
title && console.log(`--- ${title} (${issues.length})`);
|
|
25
23
|
if (issues.length) {
|
|
26
24
|
const sortedByFilePath = issues.sort((a, b) => (a.filePath > b.filePath ? 1 : -1));
|
|
27
25
|
const maxWidth = isTruncate ? TRUNCATE_WIDTH : issues.reduce((max, issue) => Math.max(issue.symbol.length, max), 0);
|
|
28
|
-
sortedByFilePath.forEach(issue => logIssueLine(issue,
|
|
26
|
+
sortedByFilePath.forEach(issue => logIssueLine(issue, maxWidth));
|
|
29
27
|
}
|
|
30
28
|
else {
|
|
31
29
|
console.log('Not found');
|
|
32
30
|
}
|
|
33
31
|
};
|
|
34
|
-
exports.default = ({ report, issues
|
|
32
|
+
exports.default = ({ report, issues }) => {
|
|
35
33
|
const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (report.unlisted) {
|
|
49
|
-
const unresolvedDependencies = Object.values(issues.unresolved).map(Object.values).flat();
|
|
50
|
-
logIssueGroupResults(unresolvedDependencies, workingDir, reportMultipleGroups && 'UNLISTED DEPENDENCIES');
|
|
51
|
-
}
|
|
52
|
-
if (report.exports) {
|
|
53
|
-
const unreferencedExports = Object.values(issues.exports).map(Object.values).flat();
|
|
54
|
-
logIssueGroupResults(unreferencedExports, workingDir, reportMultipleGroups && 'UNUSED EXPORTS');
|
|
55
|
-
}
|
|
56
|
-
if (report.nsExports) {
|
|
57
|
-
const unreferencedNsExports = Object.values(issues.nsExports).map(Object.values).flat();
|
|
58
|
-
logIssueGroupResults(unreferencedNsExports, workingDir, reportMultipleGroups && 'UNUSED EXPORTS IN NAMESPACE');
|
|
59
|
-
}
|
|
60
|
-
if (report.types) {
|
|
61
|
-
const unreferencedTypes = Object.values(issues.types).map(Object.values).flat();
|
|
62
|
-
logIssueGroupResults(unreferencedTypes, workingDir, reportMultipleGroups && 'UNUSED TYPES');
|
|
63
|
-
}
|
|
64
|
-
if (report.nsTypes) {
|
|
65
|
-
const unreferencedNsTypes = Object.values(issues.nsTypes).map(Object.values).flat();
|
|
66
|
-
logIssueGroupResults(unreferencedNsTypes, workingDir, reportMultipleGroups && 'UNUSED TYPES IN NAMESPACE');
|
|
67
|
-
}
|
|
68
|
-
if (report.duplicates) {
|
|
69
|
-
const unreferencedDuplicates = Object.values(issues.duplicates).map(Object.values).flat();
|
|
70
|
-
logIssueGroupResults(unreferencedDuplicates, workingDir, reportMultipleGroups && 'DUPLICATE EXPORTS', true);
|
|
34
|
+
for (const [reportType, isReportType] of Object.entries(report)) {
|
|
35
|
+
if (isReportType) {
|
|
36
|
+
const title = reportMultipleGroups && constants_1.ISSUE_TYPE_TITLE[reportType];
|
|
37
|
+
if (issues[reportType] instanceof Set) {
|
|
38
|
+
logIssueSet(Array.from(issues[reportType]), title);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const issuesForType = Object.values(issues[reportType]).map(Object.values).flat();
|
|
42
|
+
const isTruncate = Boolean(issuesForType[0]?.symbols?.length);
|
|
43
|
+
logIssueRecord(issuesForType, title, isTruncate);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
71
46
|
}
|
|
72
47
|
};
|
package/dist/runner.js
CHANGED
|
@@ -13,21 +13,21 @@ const dependencies_1 = require("./util/dependencies");
|
|
|
13
13
|
const debug_1 = require("./util/debug");
|
|
14
14
|
const progress_1 = require("./progress");
|
|
15
15
|
async function findIssues(configuration) {
|
|
16
|
-
const { workingDir, report, isDev, jsDocOptions } = configuration;
|
|
16
|
+
const { workingDir, report, isDev, jsDocOptions, debug } = configuration;
|
|
17
17
|
const { entryFiles, productionFiles, projectFiles, isIncludeEntryFiles } = configuration;
|
|
18
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)(
|
|
23
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
24
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
25
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
22
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Used production files', usedProductionFiles);
|
|
23
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Unreferenced production files', unreferencedProductionFiles);
|
|
24
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Used entry files', usedEntryFiles);
|
|
25
|
+
(0, debug_1.debugLogSourceFiles)(debug, 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(),
|
|
29
29
|
devDependencies: new Set(),
|
|
30
|
-
|
|
30
|
+
unlisted: {},
|
|
31
31
|
exports: {},
|
|
32
32
|
types: {},
|
|
33
33
|
nsExports: {},
|
|
@@ -38,7 +38,7 @@ async function findIssues(configuration) {
|
|
|
38
38
|
files: issues.files.size,
|
|
39
39
|
dependencies: issues.dependencies.size,
|
|
40
40
|
devDependencies: issues.dependencies.size,
|
|
41
|
-
|
|
41
|
+
unlisted: 0,
|
|
42
42
|
exports: 0,
|
|
43
43
|
types: 0,
|
|
44
44
|
nsExports: 0,
|
|
@@ -50,7 +50,7 @@ async function findIssues(configuration) {
|
|
|
50
50
|
const updateCounters = (0, progress_1.getCountersUpdater)(configuration, counters);
|
|
51
51
|
const addSymbolIssue = (issueType, issue) => {
|
|
52
52
|
const { filePath, symbol } = issue;
|
|
53
|
-
const key = node_path_1.default.relative(workingDir, filePath);
|
|
53
|
+
const key = node_path_1.default.relative(workingDir, filePath).replace(/\\/g, '/');
|
|
54
54
|
issues[issueType][key] = issues[issueType][key] ?? {};
|
|
55
55
|
issues[issueType][key][symbol] = issue;
|
|
56
56
|
counters[issueType]++;
|
|
@@ -76,7 +76,7 @@ async function findIssues(configuration) {
|
|
|
76
76
|
const filePath = sourceFile.getFilePath();
|
|
77
77
|
if (report.dependencies || report.unlisted) {
|
|
78
78
|
const unresolvedDependencies = getUnresolvedDependencies(sourceFile);
|
|
79
|
-
unresolvedDependencies.forEach(issue => addSymbolIssue('
|
|
79
|
+
unresolvedDependencies.forEach(issue => addSymbolIssue('unlisted', issue));
|
|
80
80
|
}
|
|
81
81
|
const exportDeclarations = sourceFile.getExportedDeclarations();
|
|
82
82
|
if (report.duplicates) {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { SourceFile } from 'ts-morph';
|
|
2
2
|
declare type SymbolType = 'type' | 'interface' | 'enum';
|
|
3
|
-
declare type UnusedFileIssues = Set<string>;
|
|
4
|
-
declare type UnusedExportIssues = Record<string, Record<string, Issue>>;
|
|
5
|
-
declare type UnresolvedDependencyIssues = Record<string, Record<string, Issue>>;
|
|
6
|
-
declare type UnusedDependencyIssues = Set<string>;
|
|
7
3
|
export declare type Issue = {
|
|
8
4
|
filePath: string;
|
|
9
5
|
symbol: string;
|
|
10
6
|
symbols?: string[];
|
|
11
7
|
symbolType?: SymbolType;
|
|
12
8
|
};
|
|
9
|
+
export declare type IssueSet = Set<string>;
|
|
10
|
+
export declare type IssueRecords = Record<string, Record<string, Issue>>;
|
|
13
11
|
export declare type Issues = {
|
|
14
|
-
files:
|
|
15
|
-
dependencies:
|
|
16
|
-
devDependencies:
|
|
17
|
-
|
|
18
|
-
exports:
|
|
19
|
-
types:
|
|
20
|
-
nsExports:
|
|
21
|
-
nsTypes:
|
|
22
|
-
duplicates:
|
|
12
|
+
files: IssueSet;
|
|
13
|
+
dependencies: IssueSet;
|
|
14
|
+
devDependencies: IssueSet;
|
|
15
|
+
unlisted: IssueRecords;
|
|
16
|
+
exports: IssueRecords;
|
|
17
|
+
types: IssueRecords;
|
|
18
|
+
nsExports: IssueRecords;
|
|
19
|
+
nsTypes: IssueRecords;
|
|
20
|
+
duplicates: IssueRecords;
|
|
23
21
|
};
|
|
24
|
-
declare type IssueType = keyof Issues;
|
|
22
|
+
export declare type IssueType = keyof Issues;
|
|
25
23
|
export declare type ProjectIssueType = Extract<IssueType, 'files' | 'dependencies' | 'devDependencies'>;
|
|
26
24
|
export declare type SymbolIssueType = Exclude<IssueType, ProjectIssueType>;
|
|
27
|
-
export declare type
|
|
25
|
+
export declare type Report = {
|
|
26
|
+
[key in keyof Issues]: boolean;
|
|
27
|
+
};
|
|
28
28
|
declare type BaseLocalConfiguration = {
|
|
29
29
|
entryFiles: string[];
|
|
30
30
|
projectFiles: string[];
|
|
@@ -53,9 +53,6 @@ export declare type UnresolvedConfiguration = {
|
|
|
53
53
|
level: number;
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
|
-
export declare type Report = {
|
|
57
|
-
[key in IssueGroup]: boolean;
|
|
58
|
-
};
|
|
59
56
|
export declare type Configuration = {
|
|
60
57
|
workingDir: string;
|
|
61
58
|
report: Report;
|
|
@@ -68,7 +65,7 @@ export declare type Configuration = {
|
|
|
68
65
|
optionalDependencies: string[];
|
|
69
66
|
devDependencies: string[];
|
|
70
67
|
isDev: boolean;
|
|
71
|
-
|
|
68
|
+
tsConfigPathGlobs: string[];
|
|
72
69
|
isShowProgress: boolean;
|
|
73
70
|
jsDocOptions: {
|
|
74
71
|
isReadPublicTag: boolean;
|
package/dist/util/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ImportedConfiguration, LocalConfiguration } from '../types';
|
|
1
|
+
import type { ImportedConfiguration, LocalConfiguration, Report } from '../types';
|
|
2
2
|
export declare const resolveConfig: (importedConfiguration: ImportedConfiguration, options?: {
|
|
3
3
|
workingDir?: string;
|
|
4
4
|
isDev?: boolean;
|
|
5
5
|
}) => LocalConfiguration | undefined;
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const resolveIncludedIssueTypes: (includeArg: string[], excludeArg: string[], resolvedConfig?: LocalConfiguration) => Report;
|
package/dist/util/config.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.
|
|
6
|
+
exports.resolveIncludedIssueTypes = exports.resolveConfig = void 0;
|
|
7
7
|
const micromatch_1 = __importDefault(require("micromatch"));
|
|
8
8
|
const resolveConfig = (importedConfiguration, options) => {
|
|
9
9
|
if (!importedConfiguration)
|
|
@@ -24,29 +24,20 @@ const resolveConfig = (importedConfiguration, options) => {
|
|
|
24
24
|
console.info(`Add these properties at root level, or use --dir and match one of: ${configKeys.join(', ')}\n`);
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
+
resolvedConfig.dev = Boolean(typeof resolvedConfig.dev === 'boolean' ? resolvedConfig.dev : isDev);
|
|
27
28
|
return resolvedConfig;
|
|
28
29
|
};
|
|
29
30
|
exports.resolveConfig = resolveConfig;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
];
|
|
41
|
-
const include = [includeArg, resolvedConfig?.include ?? []]
|
|
42
|
-
.flat()
|
|
43
|
-
.map(value => value.split(','))
|
|
44
|
-
.flat();
|
|
45
|
-
const exclude = [excludeArg, resolvedConfig?.exclude ?? []]
|
|
46
|
-
.flat()
|
|
47
|
-
.map(value => value.split(','))
|
|
48
|
-
.flat();
|
|
49
|
-
const includes = (include.length > 0 ? include : groups).filter((group) => !exclude.includes(group));
|
|
50
|
-
return groups.reduce((r, group) => ((r[group] = includes.includes(group)), r), {});
|
|
31
|
+
const resolveIncludedIssueTypes = (includeArg, excludeArg, resolvedConfig) => {
|
|
32
|
+
const deps = resolvedConfig?.dev ? ['dependencies', 'devDependencies'] : ['dependencies'];
|
|
33
|
+
const groups = ['files', ...deps, 'unlisted', 'exports', 'types', 'nsExports', 'nsTypes', 'duplicates'];
|
|
34
|
+
const normalizedIncludesArg = includeArg.map(value => value.split(',')).flat();
|
|
35
|
+
const normalizedExcludesArg = excludeArg.map(value => value.split(',')).flat();
|
|
36
|
+
const excludes = (resolvedConfig?.exclude ?? []).filter(exclude => !normalizedIncludesArg.includes(exclude));
|
|
37
|
+
const includes = (resolvedConfig?.include ?? []).filter(include => !normalizedExcludesArg.includes(include));
|
|
38
|
+
const include = [normalizedIncludesArg, includes].flat();
|
|
39
|
+
const exclude = [normalizedExcludesArg, excludes].flat();
|
|
40
|
+
const included = (include.length > 0 ? include : groups).filter(group => !exclude.includes(group));
|
|
41
|
+
return groups.reduce((types, group) => ((types[group] = included.includes(group)), types), {});
|
|
51
42
|
};
|
|
52
|
-
exports.
|
|
43
|
+
exports.resolveIncludedIssueTypes = resolveIncludedIssueTypes;
|
package/dist/util/debug.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { SourceFile } from 'ts-morph';
|
|
2
|
-
declare type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
level: number;
|
|
6
|
-
};
|
|
2
|
+
declare type Debug = {
|
|
3
|
+
isEnabled: boolean;
|
|
4
|
+
level: number;
|
|
7
5
|
};
|
|
8
|
-
export declare const debugLogObject: (
|
|
9
|
-
export declare const debugLogFiles: (
|
|
10
|
-
export declare const debugLogSourceFiles: (
|
|
11
|
-
export declare const debugLogDiff: (config: Config, minimumLevel: number, name: string, arrA: string[], arrB: string[]) => void;
|
|
6
|
+
export declare const debugLogObject: (debug: Debug, minimumLevel: number, name: string, obj: unknown) => void;
|
|
7
|
+
export declare const debugLogFiles: (debug: Debug, minimumLevel: number, name: string, filePaths: string[]) => void;
|
|
8
|
+
export declare const debugLogSourceFiles: (debug: Debug, minimumLevel: number, name: string, sourceFiles: SourceFile[]) => void;
|
|
12
9
|
export {};
|
package/dist/util/debug.js
CHANGED
|
@@ -3,20 +3,19 @@ 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.
|
|
6
|
+
exports.debugLogSourceFiles = exports.debugLogFiles = exports.debugLogObject = void 0;
|
|
7
7
|
const node_util_1 = __importDefault(require("node:util"));
|
|
8
8
|
const logArray = (collection) => console.log(node_util_1.default.inspect(collection, { maxArrayLength: null }));
|
|
9
|
-
const debugLogObject = (
|
|
10
|
-
if (minimumLevel >
|
|
9
|
+
const debugLogObject = (debug, minimumLevel, name, obj) => {
|
|
10
|
+
if (minimumLevel > debug.level)
|
|
11
11
|
return;
|
|
12
12
|
console.log(`[knip] ${name}:`);
|
|
13
13
|
console.log(node_util_1.default.inspect(obj, { depth: null, colors: true }));
|
|
14
14
|
};
|
|
15
15
|
exports.debugLogObject = debugLogObject;
|
|
16
|
-
const debugLogFiles = (
|
|
17
|
-
if (minimumLevel >
|
|
16
|
+
const debugLogFiles = (debug, minimumLevel, name, filePaths) => {
|
|
17
|
+
if (minimumLevel > debug.level)
|
|
18
18
|
return;
|
|
19
|
-
const { debug } = config;
|
|
20
19
|
if (debug.level > 1) {
|
|
21
20
|
console.debug(`[knip] ${name} (${filePaths.length}):`);
|
|
22
21
|
logArray(filePaths);
|
|
@@ -26,10 +25,9 @@ const debugLogFiles = (config, minimumLevel, name, filePaths) => {
|
|
|
26
25
|
}
|
|
27
26
|
};
|
|
28
27
|
exports.debugLogFiles = debugLogFiles;
|
|
29
|
-
const debugLogSourceFiles = (
|
|
30
|
-
if (minimumLevel >
|
|
28
|
+
const debugLogSourceFiles = (debug, minimumLevel, name, sourceFiles) => {
|
|
29
|
+
if (minimumLevel > debug.level)
|
|
31
30
|
return;
|
|
32
|
-
const { debug } = config;
|
|
33
31
|
if (debug.level > 1) {
|
|
34
32
|
console.debug(`[knip] ${name} (${sourceFiles.length}):`);
|
|
35
33
|
logArray(sourceFiles.map(sourceFile => sourceFile.getFilePath()));
|
|
@@ -39,8 +37,8 @@ const debugLogSourceFiles = (config, minimumLevel, name, sourceFiles) => {
|
|
|
39
37
|
}
|
|
40
38
|
};
|
|
41
39
|
exports.debugLogSourceFiles = debugLogSourceFiles;
|
|
42
|
-
const debugLogDiff = (
|
|
43
|
-
if (minimumLevel >
|
|
40
|
+
const debugLogDiff = (debug, minimumLevel, name, arrA, arrB) => {
|
|
41
|
+
if (minimumLevel > debug.level)
|
|
44
42
|
return;
|
|
45
43
|
const onlyInA = arrA.filter(itemA => !arrB.includes(itemA)).sort();
|
|
46
44
|
const onlyInB = arrB.filter(itemB => !arrA.includes(itemB)).sort();
|
|
@@ -51,4 +49,3 @@ const debugLogDiff = (config, minimumLevel, name, arrA, arrB) => {
|
|
|
51
49
|
console.log(`[knip] Only in right:`);
|
|
52
50
|
logArray(onlyInB);
|
|
53
51
|
};
|
|
54
|
-
exports.debugLogDiff = debugLogDiff;
|
|
@@ -7,29 +7,35 @@ exports.getDependencyAnalyzer = void 0;
|
|
|
7
7
|
const ts_morph_1 = require("ts-morph");
|
|
8
8
|
const is_builtin_module_1 = __importDefault(require("is-builtin-module"));
|
|
9
9
|
const micromatch_1 = __importDefault(require("micromatch"));
|
|
10
|
+
const ts_morph_helpers_1 = require("ts-morph-helpers");
|
|
10
11
|
const compact = (collection) => Array.from(new Set(collection)).filter((value) => Boolean(value));
|
|
12
|
+
const findRequireModuleSpecifiers = (sourceFile) => (0, ts_morph_helpers_1.findCallExpressionsByName)(sourceFile, 'require').map(expression => expression.getFirstDescendantByKind(ts_morph_1.ts.SyntaxKind.StringLiteral));
|
|
13
|
+
const isExternalDependency = (moduleSpecifier, tsConfigPathGlobs) => {
|
|
14
|
+
if (moduleSpecifier.startsWith('.'))
|
|
15
|
+
return false;
|
|
16
|
+
if ((0, is_builtin_module_1.default)(moduleSpecifier))
|
|
17
|
+
return false;
|
|
18
|
+
if (tsConfigPathGlobs.length > 0 && micromatch_1.default.isMatch(moduleSpecifier, tsConfigPathGlobs))
|
|
19
|
+
return false;
|
|
20
|
+
return true;
|
|
21
|
+
};
|
|
22
|
+
const resolvePackageName = (moduleSpecifier) => {
|
|
23
|
+
const parts = moduleSpecifier.split('/').slice(0, 2);
|
|
24
|
+
return moduleSpecifier.startsWith('@') ? parts.join('/') : parts[0];
|
|
25
|
+
};
|
|
11
26
|
const getDependencyAnalyzer = (configuration) => {
|
|
12
|
-
const { dependencies, devDependencies, peerDependencies, optionalDependencies,
|
|
27
|
+
const { dependencies, devDependencies, peerDependencies, optionalDependencies, tsConfigPathGlobs } = configuration;
|
|
13
28
|
const productionDependencies = [...dependencies, ...peerDependencies, ...optionalDependencies];
|
|
14
29
|
const referencedDependencies = new Set();
|
|
15
30
|
const getUnresolvedDependencies = (sourceFile) => {
|
|
16
31
|
const unresolvedDependencies = new Set();
|
|
17
32
|
const importLiterals = sourceFile.getImportStringLiterals();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const literals = compact([importLiterals, requires].flat());
|
|
23
|
-
literals.forEach(importLiteral => {
|
|
24
|
-
const moduleSpecifier = importLiteral.getLiteralText();
|
|
25
|
-
if (moduleSpecifier.startsWith('.'))
|
|
26
|
-
return;
|
|
27
|
-
if ((0, is_builtin_module_1.default)(moduleSpecifier))
|
|
28
|
-
return;
|
|
29
|
-
if (tsConfigPaths.length > 0 && micromatch_1.default.isMatch(moduleSpecifier, tsConfigPaths))
|
|
33
|
+
const requireCallExpressions = findRequireModuleSpecifiers(sourceFile);
|
|
34
|
+
const moduleSpecifiers = compact([importLiterals, requireCallExpressions].flat()).map(i => i.getLiteralText());
|
|
35
|
+
moduleSpecifiers.forEach(moduleSpecifier => {
|
|
36
|
+
if (!isExternalDependency(moduleSpecifier, tsConfigPathGlobs))
|
|
30
37
|
return;
|
|
31
|
-
const
|
|
32
|
-
const packageName = moduleSpecifier.startsWith('@') ? parts.join('/') : parts[0];
|
|
38
|
+
const packageName = resolvePackageName(moduleSpecifier);
|
|
33
39
|
if (!productionDependencies.includes(packageName) && !devDependencies.includes(packageName)) {
|
|
34
40
|
unresolvedDependencies.add({ filePath: sourceFile.getFilePath(), symbol: moduleSpecifier });
|
|
35
41
|
}
|
package/dist/util/path.d.ts
CHANGED
package/dist/util/path.js
CHANGED
|
@@ -3,8 +3,11 @@ 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.resolvePaths = void 0;
|
|
6
|
+
exports.resolvePaths = exports.relative = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
const relative = (to) => node_path_1.default.relative(cwd, to);
|
|
10
|
+
exports.relative = relative;
|
|
8
11
|
let _globby;
|
|
9
12
|
const glob = async function (patterns, options) {
|
|
10
13
|
if (!_globby) {
|
|
@@ -15,12 +18,12 @@ const glob = async function (patterns, options) {
|
|
|
15
18
|
};
|
|
16
19
|
const prependDirToPattern = (workingDir, pattern) => {
|
|
17
20
|
if (pattern.startsWith('!'))
|
|
18
|
-
return '!' + node_path_1.default.join(workingDir, pattern.slice(1));
|
|
19
|
-
return node_path_1.default.join(workingDir, pattern);
|
|
21
|
+
return '!' + node_path_1.default.posix.join(workingDir, pattern.slice(1));
|
|
22
|
+
return node_path_1.default.posix.join(workingDir, pattern);
|
|
20
23
|
};
|
|
21
|
-
const resolvePaths = async ({ cwd, workingDir, patterns, ignore, gitignore, }) => glob(patterns.map(pattern => prependDirToPattern(node_path_1.default.relative(cwd, workingDir), pattern)), {
|
|
24
|
+
const resolvePaths = async ({ cwd, workingDir, patterns, ignore, gitignore, }) => glob(patterns.map(pattern => prependDirToPattern(node_path_1.default.posix.relative(cwd, workingDir), pattern)), {
|
|
22
25
|
cwd,
|
|
23
|
-
ignore,
|
|
26
|
+
ignore: [...ignore, '**/node_modules'],
|
|
24
27
|
gitignore,
|
|
25
28
|
absolute: true,
|
|
26
29
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knip",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Find unused files, dependencies and exports in your TypeScript and JavaScript project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"find",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"knip": "dist/cli.js"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
|
-
"knip": "node ./dist/cli.js --
|
|
28
|
-
"test": "node --loader tsx --test test/*.spec.ts",
|
|
27
|
+
"knip": "node ./dist/cli.js --jsdoc public",
|
|
28
|
+
"test": "globstar -- node --loader tsx --test \"test/*.spec.ts\"",
|
|
29
29
|
"watch": "tsc --watch",
|
|
30
30
|
"build": "rm -rf dist && tsc",
|
|
31
31
|
"prepublishOnly": "npm test && npm run build && npm run knip",
|
|
@@ -50,9 +50,11 @@
|
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/micromatch": "4.0.2",
|
|
52
52
|
"@types/node": "18.11.2",
|
|
53
|
+
"globstar": "1.0.0",
|
|
53
54
|
"prettier": "2.7.1",
|
|
54
55
|
"release-it": "15.5.0",
|
|
55
56
|
"tsx": "3.10.3",
|
|
57
|
+
"type-fest": "3.1.0",
|
|
56
58
|
"typescript": "4.8.4"
|
|
57
59
|
},
|
|
58
60
|
"release-it": {
|