knip 0.9.1 → 0.11.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 +22 -6
- package/dist/help.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +24 -17
- package/dist/reporters/json.js +2 -0
- package/dist/runner.js +13 -19
- package/dist/types.d.ts +5 -5
- package/dist/util/config.d.ts +2 -2
- package/dist/util/config.js +8 -10
- package/dist/util/debug.d.ts +6 -8
- package/dist/util/debug.js +12 -19
- package/dist/util/dependencies.js +21 -15
- package/dist/util/glob.d.ts +7 -0
- package/dist/util/glob.js +29 -0
- package/dist/util/path.d.ts +0 -6
- package/dist/util/path.js +1 -21
- package/dist/util/project.d.ts +1 -0
- package/dist/util/project.js +12 -4
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -83,9 +83,10 @@ Knip works by creating two sets of files:
|
|
|
83
83
|
--ignore Ignore files matching this glob pattern (can be set multiple times)
|
|
84
84
|
--no-gitignore Don't use .gitignore
|
|
85
85
|
--dev Include `devDependencies` in report(s)
|
|
86
|
+
--include-entry-files Report unused exports and types for entry files
|
|
86
87
|
--no-progress Don't show dynamic progress updates
|
|
87
88
|
--max-issues Maximum number of issues before non-zero exit code (default: 0)
|
|
88
|
-
--reporter Select reporter: symbols, compact, codeowners (default: symbols)
|
|
89
|
+
--reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
|
|
89
90
|
--reporter-options Pass extra options to the reporter (as JSON string, see example)
|
|
90
91
|
--jsdoc Enable JSDoc parsing, with options: public
|
|
91
92
|
--debug Show debug output
|
|
@@ -125,10 +126,12 @@ following types of issues:
|
|
|
125
126
|
- `nsTypes` - Unused types in namespaces: did not find direct references to this exported variable (2)
|
|
126
127
|
- `duplicates` - Duplicate exports: the same thing is exported more than once with different names from the same file
|
|
127
128
|
|
|
128
|
-
1. This
|
|
129
|
-
|
|
129
|
+
1. This includes dependencies that could not be resolved. For instance, what does `unresolved/dir/module` mean?
|
|
130
|
+
- To target something in the (missing) `node_modules/unresolved` package?
|
|
131
|
+
- Target a local module that should have a relative path?
|
|
132
|
+
- It does not match any `paths` entry in `tsconfig.json#compilerOptions`.
|
|
130
133
|
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
|
|
134
|
+
whether this export can be removed, so please look into it.
|
|
132
135
|
|
|
133
136
|
You can `--include` or `--exclude` any of the types to slice & dice the report to your needs. Alternatively, they can be
|
|
134
137
|
added to the configuration (e.g. `"exclude": ["dependencies"]`).
|
|
@@ -140,8 +143,8 @@ As always, make sure to backup files or use Git before deleting files or making
|
|
|
140
143
|
- Unused files can be removed.
|
|
141
144
|
- Unused dependencies can be removed from `package.json`.
|
|
142
145
|
- 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
|
|
146
|
+
- Unused exports and types: remove the `export` keyword in front of unused exports. Then you (or tools such as
|
|
147
|
+
TypeScript language services in VS Code and/or ESLint) can see whether the variable or type is used within the same
|
|
145
148
|
file. If this is not the case, it can be removed.
|
|
146
149
|
|
|
147
150
|
🔁 Repeat the process to reveal new unused files and exports. Sometimes it's so liberating to remove things!
|
|
@@ -307,10 +310,23 @@ per file like this:
|
|
|
307
310
|
|
|
308
311
|
```json
|
|
309
312
|
[
|
|
313
|
+
{
|
|
314
|
+
"file": "package.json",
|
|
315
|
+
"owners": ["@org/admin"],
|
|
316
|
+
"files": false,
|
|
317
|
+
"dependencies": ["jquery", "moment"],
|
|
318
|
+
"devDependencies": [],
|
|
319
|
+
"unlisted": [],
|
|
320
|
+
"exports": [],
|
|
321
|
+
"types": [],
|
|
322
|
+
"duplicates": []
|
|
323
|
+
},
|
|
310
324
|
{
|
|
311
325
|
"file": "src/Registration.tsx",
|
|
312
326
|
"owners": ["@org/owner"],
|
|
313
327
|
"files": true,
|
|
328
|
+
"dependencies": [],
|
|
329
|
+
"devDependencies": [],
|
|
314
330
|
"unlisted": ["react"],
|
|
315
331
|
"exports": ["lowercaseFirstLetter", "RegistrationBox"],
|
|
316
332
|
"types": ["RegistrationServices", "RegistrationAction"],
|
package/dist/help.js
CHANGED
|
@@ -16,7 +16,7 @@ Options:
|
|
|
16
16
|
--include-entry-files Report unused exports and types for entry files
|
|
17
17
|
--no-progress Don't show dynamic progress updates
|
|
18
18
|
--max-issues Maximum number of issues before non-zero exit code (default: 0)
|
|
19
|
-
--reporter Select reporter: symbols, compact, codeowners (default: symbols)
|
|
19
|
+
--reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
|
|
20
20
|
--reporter-options Pass extra options to the reporter (as JSON string, see example)
|
|
21
21
|
--jsdoc Enable JSDoc parsing, with options: public
|
|
22
22
|
--debug Show debug output
|
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
|
@@ -8,18 +8,22 @@ const typescript_1 = __importDefault(require("typescript"));
|
|
|
8
8
|
const config_1 = require("./util/config");
|
|
9
9
|
const fs_1 = require("./util/fs");
|
|
10
10
|
const path_1 = require("./util/path");
|
|
11
|
+
const glob_1 = require("./util/glob");
|
|
11
12
|
const project_1 = require("./util/project");
|
|
12
13
|
const runner_1 = require("./runner");
|
|
13
14
|
const errors_1 = require("./util/errors");
|
|
14
15
|
const debug_1 = require("./util/debug");
|
|
15
16
|
const progress_1 = require("./progress");
|
|
16
|
-
const main = async (
|
|
17
|
-
const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } =
|
|
18
|
-
const updateMessage = (0, progress_1.getMessageUpdater)(
|
|
19
|
-
(0, debug_1.debugLogObject)(
|
|
17
|
+
const main = async (unresolvedConfiguration) => {
|
|
18
|
+
const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } = unresolvedConfiguration;
|
|
19
|
+
const updateMessage = (0, progress_1.getMessageUpdater)(unresolvedConfiguration);
|
|
20
|
+
(0, debug_1.debugLogObject)(debug, 1, 'Unresolved configuration', unresolvedConfiguration);
|
|
20
21
|
updateMessage('Reading configuration and manifest files...');
|
|
21
22
|
const manifestPath = await (0, fs_1.findFile)(cwd, workingDir, 'package.json');
|
|
22
23
|
const manifest = manifestPath && require(manifestPath);
|
|
24
|
+
if (!manifestPath || !manifest) {
|
|
25
|
+
throw new errors_1.ConfigurationError('Unable to find package.json');
|
|
26
|
+
}
|
|
23
27
|
const configFilePath = configFilePathArg ?? 'knip.json';
|
|
24
28
|
const resolvedConfigFilePath = await (0, fs_1.findFile)(cwd, workingDir, configFilePath);
|
|
25
29
|
const localConfig = resolvedConfigFilePath && require(resolvedConfigFilePath);
|
|
@@ -31,10 +35,10 @@ const main = async (options) => {
|
|
|
31
35
|
if (tsConfigFilePathArg && !resolvedTsConfigFilePath) {
|
|
32
36
|
throw new errors_1.ConfigurationError(`Unable to find ${tsConfigFilePathArg}`);
|
|
33
37
|
}
|
|
34
|
-
let
|
|
38
|
+
let tsConfigPathGlobs = [];
|
|
35
39
|
if (resolvedTsConfigFilePath) {
|
|
36
40
|
const config = typescript_1.default.readConfigFile(resolvedTsConfigFilePath, typescript_1.default.sys.readFile);
|
|
37
|
-
|
|
41
|
+
tsConfigPathGlobs = config.config.compilerOptions?.paths
|
|
38
42
|
? Object.keys(config.config.compilerOptions.paths).map(p => p.replace(/\*/g, '**'))
|
|
39
43
|
: [];
|
|
40
44
|
if (config.error) {
|
|
@@ -43,7 +47,7 @@ const main = async (options) => {
|
|
|
43
47
|
}
|
|
44
48
|
const dir = (0, path_1.relative)(workingDir);
|
|
45
49
|
const resolvedConfig = (0, config_1.resolveConfig)(manifest.knip ?? localConfig, { workingDir: dir, isDev });
|
|
46
|
-
(0, debug_1.debugLogObject)(
|
|
50
|
+
(0, debug_1.debugLogObject)(debug, 1, 'Resolved configuration', resolvedConfig);
|
|
47
51
|
if (!resolvedConfigFilePath && !manifest.knip && !resolvedTsConfigFilePath) {
|
|
48
52
|
throw new errors_1.ConfigurationError(`Unable to find ${configFilePath} or package.json#knip or ${tsConfigFilePath}`);
|
|
49
53
|
}
|
|
@@ -54,30 +58,32 @@ const main = async (options) => {
|
|
|
54
58
|
? { tsConfigFilePath: resolvedTsConfigFilePath }
|
|
55
59
|
: { compilerOptions: { allowJs: true } };
|
|
56
60
|
updateMessage('Resolving entry files...');
|
|
57
|
-
const entryPaths = await (0,
|
|
61
|
+
const entryPaths = await (0, glob_1.glob)({
|
|
62
|
+
cwd,
|
|
58
63
|
workingDir,
|
|
59
64
|
patterns: resolvedConfig.entryFiles,
|
|
60
65
|
ignore,
|
|
61
66
|
gitignore,
|
|
62
67
|
});
|
|
63
|
-
(0, debug_1.debugLogFiles)(
|
|
68
|
+
(0, debug_1.debugLogFiles)(debug, 1, 'Globbed entry paths', entryPaths);
|
|
64
69
|
const production = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, entryPaths);
|
|
65
70
|
const entryFiles = production.getSourceFiles();
|
|
66
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
71
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Resolved entry source files', entryFiles);
|
|
67
72
|
production.resolveSourceFileDependencies();
|
|
68
|
-
const productionFiles =
|
|
69
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
73
|
+
const productionFiles = (0, project_1.removeExternalSourceFiles)(production);
|
|
74
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Resolved production source files', productionFiles);
|
|
70
75
|
updateMessage('Resolving project files...');
|
|
71
|
-
const projectPaths = await (0,
|
|
76
|
+
const projectPaths = await (0, glob_1.glob)({
|
|
77
|
+
cwd,
|
|
72
78
|
workingDir,
|
|
73
79
|
patterns: resolvedConfig.projectFiles,
|
|
74
80
|
ignore,
|
|
75
81
|
gitignore,
|
|
76
82
|
});
|
|
77
|
-
(0, debug_1.debugLogFiles)(
|
|
83
|
+
(0, debug_1.debugLogFiles)(debug, 1, 'Globbed project paths', projectPaths);
|
|
78
84
|
const project = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, projectPaths);
|
|
79
85
|
const projectFiles = project.getSourceFiles();
|
|
80
|
-
(0, debug_1.debugLogSourceFiles)(
|
|
86
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Resolved project source files', projectFiles);
|
|
81
87
|
return { entryFiles, productionFiles, projectFiles };
|
|
82
88
|
}
|
|
83
89
|
else {
|
|
@@ -95,12 +101,13 @@ const main = async (options) => {
|
|
|
95
101
|
productionFiles,
|
|
96
102
|
projectFiles,
|
|
97
103
|
isIncludeEntryFiles: !resolvedConfig || isIncludeEntryFiles,
|
|
104
|
+
manifestPath,
|
|
98
105
|
dependencies: Object.keys(manifest.dependencies ?? {}),
|
|
99
106
|
peerDependencies: Object.keys(manifest.peerDependencies ?? {}),
|
|
100
107
|
optionalDependencies: Object.keys(manifest.optionalDependencies ?? {}),
|
|
101
108
|
devDependencies: Object.keys(manifest.devDependencies ?? {}),
|
|
102
109
|
isDev: Boolean(resolvedConfig?.dev),
|
|
103
|
-
|
|
110
|
+
tsConfigPathGlobs: tsConfigPathGlobs,
|
|
104
111
|
isShowProgress,
|
|
105
112
|
jsDocOptions: {
|
|
106
113
|
isReadPublicTag: jsDoc.includes('public'),
|
|
@@ -108,7 +115,7 @@ const main = async (options) => {
|
|
|
108
115
|
debug,
|
|
109
116
|
};
|
|
110
117
|
const { issues, counters } = await (0, runner_1.findIssues)(config);
|
|
111
|
-
(0, debug_1.debugLogObject)(
|
|
118
|
+
(0, debug_1.debugLogObject)(debug, 2, 'Issues', issues);
|
|
112
119
|
return { report, issues, counters };
|
|
113
120
|
};
|
|
114
121
|
exports.main = main;
|
package/dist/reporters/json.js
CHANGED
|
@@ -26,6 +26,8 @@ exports.default = async ({ report, issues, options }) => {
|
|
|
26
26
|
file,
|
|
27
27
|
...(codeownersEngine && { owners: codeownersEngine.calcFileOwnership(file) }),
|
|
28
28
|
...(report.files && { files: false }),
|
|
29
|
+
...(report.dependencies && { dependencies: [] }),
|
|
30
|
+
...(report.devDependencies && { devDependencies: [] }),
|
|
29
31
|
...(report.unlisted && { unlisted: [] }),
|
|
30
32
|
...((report.exports || report.nsExports) && { exports: [] }),
|
|
31
33
|
...((report.types || report.nsTypes) && { types: [] }),
|
package/dist/runner.js
CHANGED
|
@@ -13,20 +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
|
+
const { manifestPath } = configuration;
|
|
18
19
|
const updateMessage = (0, progress_1.getMessageUpdater)(configuration);
|
|
19
20
|
const { getUnresolvedDependencies, getUnusedDependencies, getUnusedDevDependencies } = (0, dependencies_1.getDependencyAnalyzer)(configuration);
|
|
20
21
|
const [usedProductionFiles, unreferencedProductionFiles] = (0, project_1.partitionSourceFiles)(projectFiles, productionFiles);
|
|
21
22
|
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)(
|
|
23
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Used production files', usedProductionFiles);
|
|
24
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Unreferenced production files', unreferencedProductionFiles);
|
|
25
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Used entry files', usedEntryFiles);
|
|
26
|
+
(0, debug_1.debugLogSourceFiles)(debug, 1, 'Used non-entry files', usedNonEntryFiles);
|
|
26
27
|
const issues = {
|
|
27
28
|
files: new Set(unreferencedProductionFiles.map(file => file.getFilePath())),
|
|
28
|
-
dependencies:
|
|
29
|
-
devDependencies:
|
|
29
|
+
dependencies: {},
|
|
30
|
+
devDependencies: {},
|
|
30
31
|
unlisted: {},
|
|
31
32
|
exports: {},
|
|
32
33
|
types: {},
|
|
@@ -36,8 +37,8 @@ async function findIssues(configuration) {
|
|
|
36
37
|
};
|
|
37
38
|
const counters = {
|
|
38
39
|
files: issues.files.size,
|
|
39
|
-
dependencies:
|
|
40
|
-
devDependencies:
|
|
40
|
+
dependencies: 0,
|
|
41
|
+
devDependencies: 0,
|
|
41
42
|
unlisted: 0,
|
|
42
43
|
exports: 0,
|
|
43
44
|
types: 0,
|
|
@@ -50,19 +51,12 @@ async function findIssues(configuration) {
|
|
|
50
51
|
const updateCounters = (0, progress_1.getCountersUpdater)(configuration, counters);
|
|
51
52
|
const addSymbolIssue = (issueType, issue) => {
|
|
52
53
|
const { filePath, symbol } = issue;
|
|
53
|
-
const key = node_path_1.default.relative(workingDir, filePath);
|
|
54
|
+
const key = node_path_1.default.relative(workingDir, filePath).replace(/\\/g, '/');
|
|
54
55
|
issues[issueType][key] = issues[issueType][key] ?? {};
|
|
55
56
|
issues[issueType][key][symbol] = issue;
|
|
56
57
|
counters[issueType]++;
|
|
57
58
|
updateCounters(issue);
|
|
58
59
|
};
|
|
59
|
-
const addProjectIssue = (issueType, issue) => {
|
|
60
|
-
if (!issues[issueType].has(issue.symbol)) {
|
|
61
|
-
issues[issueType].add(issue.symbol);
|
|
62
|
-
counters[issueType]++;
|
|
63
|
-
}
|
|
64
|
-
updateCounters(issue);
|
|
65
|
-
};
|
|
66
60
|
updateMessage('Connecting the dots...');
|
|
67
61
|
if (report.dependencies ||
|
|
68
62
|
report.unlisted ||
|
|
@@ -174,10 +168,10 @@ async function findIssues(configuration) {
|
|
|
174
168
|
}
|
|
175
169
|
if (report.dependencies) {
|
|
176
170
|
const unusedDependencies = getUnusedDependencies();
|
|
177
|
-
unusedDependencies.forEach(symbol =>
|
|
171
|
+
unusedDependencies.forEach(symbol => addSymbolIssue('dependencies', { filePath: manifestPath, symbol }));
|
|
178
172
|
if (isDev) {
|
|
179
173
|
const unusedDevDependencies = getUnusedDevDependencies();
|
|
180
|
-
unusedDevDependencies.forEach(symbol =>
|
|
174
|
+
unusedDevDependencies.forEach(symbol => addSymbolIssue('devDependencies', { filePath: manifestPath, symbol }));
|
|
181
175
|
}
|
|
182
176
|
}
|
|
183
177
|
updateCounters();
|
package/dist/types.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export declare type IssueSet = Set<string>;
|
|
|
10
10
|
export declare type IssueRecords = Record<string, Record<string, Issue>>;
|
|
11
11
|
export declare type Issues = {
|
|
12
12
|
files: IssueSet;
|
|
13
|
-
dependencies:
|
|
14
|
-
devDependencies:
|
|
13
|
+
dependencies: IssueRecords;
|
|
14
|
+
devDependencies: IssueRecords;
|
|
15
15
|
unlisted: IssueRecords;
|
|
16
16
|
exports: IssueRecords;
|
|
17
17
|
types: IssueRecords;
|
|
@@ -20,8 +20,7 @@ export declare type Issues = {
|
|
|
20
20
|
duplicates: IssueRecords;
|
|
21
21
|
};
|
|
22
22
|
export declare type IssueType = keyof Issues;
|
|
23
|
-
export declare type
|
|
24
|
-
export declare type SymbolIssueType = Exclude<IssueType, ProjectIssueType>;
|
|
23
|
+
export declare type SymbolIssueType = Exclude<IssueType, 'files'>;
|
|
25
24
|
export declare type Report = {
|
|
26
25
|
[key in keyof Issues]: boolean;
|
|
27
26
|
};
|
|
@@ -60,12 +59,13 @@ export declare type Configuration = {
|
|
|
60
59
|
productionFiles: SourceFile[];
|
|
61
60
|
entryFiles: SourceFile[];
|
|
62
61
|
isIncludeEntryFiles: boolean;
|
|
62
|
+
manifestPath: string;
|
|
63
63
|
dependencies: string[];
|
|
64
64
|
peerDependencies: string[];
|
|
65
65
|
optionalDependencies: string[];
|
|
66
66
|
devDependencies: string[];
|
|
67
67
|
isDev: boolean;
|
|
68
|
-
|
|
68
|
+
tsConfigPathGlobs: string[];
|
|
69
69
|
isShowProgress: boolean;
|
|
70
70
|
jsDocOptions: {
|
|
71
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 resolveIncludedIssueTypes: (includeArg: string[], excludeArg: string[], resolvedConfig?: LocalConfiguration) =>
|
|
6
|
+
export declare const resolveIncludedIssueTypes: (includeArg: string[], excludeArg: string[], resolvedConfig?: LocalConfiguration) => Report;
|
package/dist/util/config.js
CHANGED
|
@@ -31,15 +31,13 @@ exports.resolveConfig = resolveConfig;
|
|
|
31
31
|
const resolveIncludedIssueTypes = (includeArg, excludeArg, resolvedConfig) => {
|
|
32
32
|
const deps = resolvedConfig?.dev ? ['dependencies', 'devDependencies'] : ['dependencies'];
|
|
33
33
|
const groups = ['files', ...deps, 'unlisted', 'exports', 'types', 'nsExports', 'nsTypes', 'duplicates'];
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const includes = (include.length > 0 ? include : groups).filter(group => !exclude.includes(group));
|
|
43
|
-
return groups.reduce((r, group) => ((r[group] = includes.includes(group)), r), {});
|
|
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), {});
|
|
44
42
|
};
|
|
45
43
|
exports.resolveIncludedIssueTypes = resolveIncludedIssueTypes;
|
package/dist/util/debug.d.ts
CHANGED
|
@@ -1,11 +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: (
|
|
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;
|
|
11
9
|
export {};
|
package/dist/util/debug.js
CHANGED
|
@@ -6,41 +6,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
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
|
-
|
|
19
|
+
console.debug(`[knip] ${name} (${filePaths.length}):`);
|
|
20
20
|
if (debug.level > 1) {
|
|
21
|
-
console.debug(`[knip] ${name} (${filePaths.length}):`);
|
|
22
21
|
logArray(filePaths);
|
|
23
22
|
}
|
|
24
|
-
else {
|
|
25
|
-
console.debug(`[knip] ${name} (${filePaths.length})`);
|
|
26
|
-
}
|
|
27
23
|
};
|
|
28
24
|
exports.debugLogFiles = debugLogFiles;
|
|
29
|
-
const debugLogSourceFiles = (
|
|
30
|
-
if (minimumLevel >
|
|
25
|
+
const debugLogSourceFiles = (debug, minimumLevel, name, sourceFiles) => {
|
|
26
|
+
if (minimumLevel > debug.level)
|
|
31
27
|
return;
|
|
32
|
-
|
|
28
|
+
console.debug(`[knip] ${name} (${sourceFiles.length})`);
|
|
33
29
|
if (debug.level > 1) {
|
|
34
|
-
|
|
35
|
-
logArray(
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
console.debug(`[knip] ${name} (${sourceFiles.length})`);
|
|
30
|
+
const files = sourceFiles.map(sourceFile => sourceFile.getFilePath());
|
|
31
|
+
logArray(files);
|
|
39
32
|
}
|
|
40
33
|
};
|
|
41
34
|
exports.debugLogSourceFiles = debugLogSourceFiles;
|
|
42
|
-
const debugLogDiff = (
|
|
43
|
-
if (minimumLevel >
|
|
35
|
+
const debugLogDiff = (debug, minimumLevel, name, arrA, arrB) => {
|
|
36
|
+
if (minimumLevel > debug.level)
|
|
44
37
|
return;
|
|
45
38
|
const onlyInA = arrA.filter(itemA => !arrB.includes(itemA)).sort();
|
|
46
39
|
const onlyInB = arrB.filter(itemB => !arrA.includes(itemB)).sort();
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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.glob = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
let _globby;
|
|
9
|
+
const globProxy = async function (patterns, options) {
|
|
10
|
+
if (!_globby) {
|
|
11
|
+
const { globby } = await eval('import("globby")');
|
|
12
|
+
_globby = globby;
|
|
13
|
+
}
|
|
14
|
+
return _globby(patterns, options);
|
|
15
|
+
};
|
|
16
|
+
const prependDirToPattern = (workingDir, pattern) => {
|
|
17
|
+
if (pattern.startsWith('!'))
|
|
18
|
+
return '!' + node_path_1.default.posix.join(workingDir, pattern.slice(1));
|
|
19
|
+
return node_path_1.default.posix.join(workingDir, pattern);
|
|
20
|
+
};
|
|
21
|
+
const glob = async ({ cwd, workingDir, patterns, ignore, gitignore, }) => {
|
|
22
|
+
return globProxy(patterns.map(pattern => prependDirToPattern(node_path_1.default.posix.relative(cwd, workingDir), pattern)), {
|
|
23
|
+
cwd,
|
|
24
|
+
ignore: [...ignore, '**/node_modules/**'],
|
|
25
|
+
gitignore,
|
|
26
|
+
absolute: true,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
exports.glob = glob;
|
package/dist/util/path.d.ts
CHANGED
package/dist/util/path.js
CHANGED
|
@@ -3,28 +3,8 @@ 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.relative = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
8
|
const cwd = process.cwd();
|
|
9
9
|
const relative = (to) => node_path_1.default.relative(cwd, to);
|
|
10
10
|
exports.relative = relative;
|
|
11
|
-
let _globby;
|
|
12
|
-
const glob = async function (patterns, options) {
|
|
13
|
-
if (!_globby) {
|
|
14
|
-
const { globby } = await eval('import("globby")');
|
|
15
|
-
_globby = globby;
|
|
16
|
-
}
|
|
17
|
-
return _globby(patterns, options);
|
|
18
|
-
};
|
|
19
|
-
const prependDirToPattern = (workingDir, pattern) => {
|
|
20
|
-
if (pattern.startsWith('!'))
|
|
21
|
-
return '!' + node_path_1.default.join(workingDir, pattern.slice(1));
|
|
22
|
-
return node_path_1.default.join(workingDir, pattern);
|
|
23
|
-
};
|
|
24
|
-
const resolvePaths = async ({ workingDir, patterns, ignore, gitignore, }) => glob(patterns.map(pattern => prependDirToPattern((0, exports.relative)(workingDir), pattern)), {
|
|
25
|
-
cwd,
|
|
26
|
-
ignore,
|
|
27
|
-
gitignore,
|
|
28
|
-
absolute: true,
|
|
29
|
-
});
|
|
30
|
-
exports.resolvePaths = resolvePaths;
|
package/dist/util/project.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Project } from 'ts-morph';
|
|
2
2
|
import type { ProjectOptions, SourceFile } from 'ts-morph';
|
|
3
3
|
export declare const createProject: (projectOptions: ProjectOptions, paths?: string[]) => Project;
|
|
4
|
+
export declare const removeExternalSourceFiles: (project: Project) => SourceFile[];
|
|
4
5
|
export declare const partitionSourceFiles: (projectFiles: SourceFile[], productionFiles: SourceFile[]) => SourceFile[][];
|
package/dist/util/project.js
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.partitionSourceFiles = exports.createProject = void 0;
|
|
3
|
+
exports.partitionSourceFiles = exports.removeExternalSourceFiles = exports.createProject = void 0;
|
|
4
4
|
const ts_morph_1 = require("ts-morph");
|
|
5
5
|
const createProject = (projectOptions, paths) => {
|
|
6
|
-
const
|
|
6
|
+
const project = new ts_morph_1.Project(projectOptions);
|
|
7
7
|
if (paths)
|
|
8
|
-
|
|
9
|
-
return
|
|
8
|
+
project.addSourceFilesAtPaths(paths);
|
|
9
|
+
return project;
|
|
10
10
|
};
|
|
11
11
|
exports.createProject = createProject;
|
|
12
|
+
const removeExternalSourceFiles = (project) => project.getSourceFiles().filter(sourceFile => {
|
|
13
|
+
if (/\/node_modules\//.test(sourceFile.getFilePath())) {
|
|
14
|
+
project.removeSourceFile(sourceFile);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
});
|
|
19
|
+
exports.removeExternalSourceFiles = removeExternalSourceFiles;
|
|
12
20
|
const partitionSourceFiles = (projectFiles, productionFiles) => {
|
|
13
21
|
const productionFilePaths = productionFiles.map(file => file.getFilePath());
|
|
14
22
|
const usedFiles = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knip",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Find unused files, dependencies and exports in your TypeScript and JavaScript project",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"find",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"knip": "node ./dist/cli.js --jsdoc public",
|
|
28
|
-
"test": "node --loader tsx --test test/*.spec.ts",
|
|
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,6 +50,7 @@
|
|
|
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",
|