knip 2.3.2 → 2.6.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 +139 -133
- package/dist/ConfigurationChief.d.ts +15 -0
- package/dist/ConfigurationChief.js +10 -1
- package/dist/ConfigurationValidator.d.ts +6 -3
- package/dist/ConfigurationValidator.js +16 -0
- package/dist/IssueCollector.d.ts +12 -7
- package/dist/IssueCollector.js +7 -3
- package/dist/PrincipalFactory.d.ts +1 -3
- package/dist/PrincipalFactory.js +6 -6
- package/dist/ProjectPrincipal.d.ts +4 -5
- package/dist/ProjectPrincipal.js +2 -5
- package/dist/WorkspaceWorker.js +4 -3
- package/dist/binaries/resolvers/fallback.js +4 -2
- package/dist/binaries/resolvers/yarn.js +1 -0
- package/dist/cli.js +2 -2
- package/dist/constants.js +2 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +47 -42
- package/dist/issues/initializers.d.ts +2 -1
- package/dist/issues/initializers.js +1 -0
- package/dist/plugins/eslint/helpers.js +3 -3
- package/dist/plugins/eslint/types.d.ts +1 -1
- package/dist/plugins/jest/index.js +2 -3
- package/dist/reporters/codeowners.js +2 -2
- package/dist/reporters/compact.js +1 -1
- package/dist/reporters/symbols.js +7 -5
- package/dist/reporters/util.d.ts +4 -1
- package/dist/reporters/util.js +6 -4
- package/dist/types/config.d.ts +2 -0
- package/dist/types/issues.d.ts +3 -0
- package/dist/typescript/visitors/scripts/execa.d.ts +3 -0
- package/dist/typescript/visitors/scripts/execa.js +16 -0
- package/dist/typescript/visitors/scripts/index.js +2 -1
- package/dist/util/compilers.d.ts +2 -1
- package/dist/util/object.d.ts +1 -0
- package/dist/util/object.js +8 -0
- package/dist/util/path.d.ts +5 -4
- package/dist/util/path.js +6 -4
- package/dist/util/tsconfig-loader.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -6
- package/schema.json +20 -0
package/dist/IssueCollector.d.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import type { Issue } from './types/issues.js';
|
|
1
|
+
import type { Issue, Rules } from './types/issues.js';
|
|
2
2
|
type IssueCollectorOptions = {
|
|
3
3
|
cwd: string;
|
|
4
|
+
rules: Rules;
|
|
4
5
|
};
|
|
5
6
|
export declare class IssueCollector {
|
|
6
|
-
cwd
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
private cwd;
|
|
8
|
+
private rules;
|
|
9
|
+
private issues;
|
|
10
|
+
private counters;
|
|
11
|
+
private referencedFiles;
|
|
12
|
+
constructor({ cwd, rules }: IssueCollectorOptions);
|
|
13
|
+
addFileCounts({ processed, unused }: {
|
|
14
|
+
processed: number;
|
|
15
|
+
unused: number;
|
|
16
|
+
}): void;
|
|
12
17
|
addFilesIssues(filePaths: string[]): void;
|
|
13
18
|
addIssue(issue: Issue): void;
|
|
14
19
|
getIssues(): {
|
package/dist/IssueCollector.js
CHANGED
|
@@ -2,14 +2,17 @@ import { initIssues, initCounters } from './issues/initializers.js';
|
|
|
2
2
|
import { relative } from './util/path.js';
|
|
3
3
|
export class IssueCollector {
|
|
4
4
|
cwd;
|
|
5
|
+
rules;
|
|
5
6
|
issues = initIssues();
|
|
6
7
|
counters = initCounters();
|
|
7
8
|
referencedFiles = new Set();
|
|
8
|
-
constructor({ cwd }) {
|
|
9
|
+
constructor({ cwd, rules }) {
|
|
9
10
|
this.cwd = cwd;
|
|
11
|
+
this.rules = rules;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
this.counters.
|
|
13
|
+
addFileCounts({ processed, unused }) {
|
|
14
|
+
this.counters.processed += processed;
|
|
15
|
+
this.counters.total += processed + unused;
|
|
13
16
|
}
|
|
14
17
|
addFilesIssues(filePaths) {
|
|
15
18
|
filePaths.forEach(filePath => {
|
|
@@ -22,6 +25,7 @@ export class IssueCollector {
|
|
|
22
25
|
}
|
|
23
26
|
addIssue(issue) {
|
|
24
27
|
const key = relative(this.cwd, issue.filePath);
|
|
28
|
+
issue.severity = this.rules[issue.type];
|
|
25
29
|
this.issues[issue.type][key] = this.issues[issue.type][key] ?? {};
|
|
26
30
|
if (!this.issues[issue.type][key][issue.symbol]) {
|
|
27
31
|
this.issues[issue.type][key][issue.symbol] = issue;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { ProjectPrincipal } from './ProjectPrincipal.js';
|
|
3
3
|
import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
|
|
4
|
-
import type { Report } from './types/issues.js';
|
|
5
4
|
type Paths = ts.CompilerOptions['paths'];
|
|
6
5
|
type Principal = {
|
|
7
6
|
principal: ProjectPrincipal;
|
|
@@ -12,13 +11,12 @@ type Principals = Set<Principal>;
|
|
|
12
11
|
type Options = {
|
|
13
12
|
cwd: string;
|
|
14
13
|
compilerOptions: ts.CompilerOptions;
|
|
15
|
-
report: Report;
|
|
16
14
|
paths: Paths;
|
|
17
15
|
compilers: [SyncCompilers, AsyncCompilers];
|
|
18
16
|
};
|
|
19
17
|
export declare class PrincipalFactory {
|
|
20
18
|
principals: Principals;
|
|
21
|
-
getPrincipal({ cwd, compilerOptions,
|
|
19
|
+
getPrincipal({ cwd, compilerOptions, paths, compilers }: Options): ProjectPrincipal;
|
|
22
20
|
private findReusablePrincipal;
|
|
23
21
|
private linkPrincipal;
|
|
24
22
|
private addNewPrincipal;
|
package/dist/PrincipalFactory.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ProjectPrincipal } from './ProjectPrincipal.js';
|
|
2
|
-
import { join,
|
|
2
|
+
import { join, toAbsolute } from './util/path.js';
|
|
3
3
|
const mergePaths = (cwd, compilerOptions, paths = {}) => {
|
|
4
4
|
const overridePaths = Object.keys(paths).reduce((overridePaths, key) => {
|
|
5
|
-
overridePaths[key] = paths[key].map(entry => (
|
|
5
|
+
overridePaths[key] = paths[key].map(entry => toAbsolute(entry, cwd));
|
|
6
6
|
return overridePaths;
|
|
7
7
|
}, {});
|
|
8
8
|
compilerOptions.paths = { ...compilerOptions.paths, ...overridePaths };
|
|
@@ -10,7 +10,7 @@ const mergePaths = (cwd, compilerOptions, paths = {}) => {
|
|
|
10
10
|
};
|
|
11
11
|
export class PrincipalFactory {
|
|
12
12
|
principals = new Set();
|
|
13
|
-
getPrincipal({ cwd, compilerOptions,
|
|
13
|
+
getPrincipal({ cwd, compilerOptions, paths, compilers }) {
|
|
14
14
|
compilerOptions = mergePaths(cwd, compilerOptions, paths);
|
|
15
15
|
const principal = this.findReusablePrincipal(compilerOptions);
|
|
16
16
|
if (principal) {
|
|
@@ -18,7 +18,7 @@ export class PrincipalFactory {
|
|
|
18
18
|
return principal.principal;
|
|
19
19
|
}
|
|
20
20
|
else {
|
|
21
|
-
return this.addNewPrincipal({ cwd, compilerOptions,
|
|
21
|
+
return this.addNewPrincipal({ cwd, compilerOptions, paths, compilers });
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
findReusablePrincipal(compilerOptions) {
|
|
@@ -36,8 +36,8 @@ export class PrincipalFactory {
|
|
|
36
36
|
principal.principal.compilerOptions.paths = { ...principal.principal.compilerOptions.paths, ...paths };
|
|
37
37
|
principal.cwds.add(cwd);
|
|
38
38
|
}
|
|
39
|
-
addNewPrincipal({ cwd, compilerOptions,
|
|
40
|
-
const principal = new ProjectPrincipal({ cwd, compilerOptions,
|
|
39
|
+
addNewPrincipal({ cwd, compilerOptions, compilers }) {
|
|
40
|
+
const principal = new ProjectPrincipal({ cwd, compilerOptions, compilers });
|
|
41
41
|
const pathKeys = new Set(Object.keys(compilerOptions?.paths ?? {}));
|
|
42
42
|
compilerOptions.baseUrl = join(cwd, compilerOptions.baseUrl ?? '.');
|
|
43
43
|
this.principals.add({ principal, cwds: new Set([cwd]), pathKeys });
|
|
@@ -2,11 +2,9 @@ import ts from 'typescript';
|
|
|
2
2
|
import { SourceFileManager } from './typescript/SourceFileManager.js';
|
|
3
3
|
import type { SyncCompilers, AsyncCompilers } from './types/compilers.js';
|
|
4
4
|
import type { ExportItem, ExportItemMember } from './types/exports.js';
|
|
5
|
-
import type { Report } from './types/issues.js';
|
|
6
5
|
type ProjectPrincipalOptions = {
|
|
7
6
|
compilerOptions: ts.CompilerOptions;
|
|
8
7
|
cwd: string;
|
|
9
|
-
report: Report;
|
|
10
8
|
compilers: [SyncCompilers, AsyncCompilers];
|
|
11
9
|
};
|
|
12
10
|
export declare class ProjectPrincipal {
|
|
@@ -15,7 +13,6 @@ export declare class ProjectPrincipal {
|
|
|
15
13
|
skipExportsAnalysis: Set<string>;
|
|
16
14
|
cwd: string;
|
|
17
15
|
compilerOptions: ts.CompilerOptions;
|
|
18
|
-
isReportTypes: boolean;
|
|
19
16
|
extensions: Set<string>;
|
|
20
17
|
syncCompilers: SyncCompilers;
|
|
21
18
|
asyncCompilers: AsyncCompilers;
|
|
@@ -26,7 +23,7 @@ export declare class ProjectPrincipal {
|
|
|
26
23
|
lsFindReferences: ts.LanguageService['findReferences'];
|
|
27
24
|
program?: ts.Program;
|
|
28
25
|
};
|
|
29
|
-
constructor({ compilerOptions, cwd,
|
|
26
|
+
constructor({ compilerOptions, cwd, compilers }: ProjectPrincipalOptions);
|
|
30
27
|
private createProgram;
|
|
31
28
|
private hasAcceptedExtension;
|
|
32
29
|
addEntryPath(filePath: string): void;
|
|
@@ -37,7 +34,9 @@ export declare class ProjectPrincipal {
|
|
|
37
34
|
getUsedResolvedFiles(): string[];
|
|
38
35
|
private getProgramSourceFiles;
|
|
39
36
|
getUnreferencedFiles(): string[];
|
|
40
|
-
analyzeSourceFile(filePath: string
|
|
37
|
+
analyzeSourceFile(filePath: string, { skipTypeOnly }: {
|
|
38
|
+
skipTypeOnly: boolean;
|
|
39
|
+
}): {
|
|
41
40
|
imports: {
|
|
42
41
|
internal: import("./types/imports.js").Imports;
|
|
43
42
|
unresolved: Set<string>;
|
package/dist/ProjectPrincipal.js
CHANGED
|
@@ -25,12 +25,11 @@ export class ProjectPrincipal {
|
|
|
25
25
|
skipExportsAnalysis = new Set();
|
|
26
26
|
cwd;
|
|
27
27
|
compilerOptions;
|
|
28
|
-
isReportTypes;
|
|
29
28
|
extensions;
|
|
30
29
|
syncCompilers;
|
|
31
30
|
asyncCompilers;
|
|
32
31
|
backend;
|
|
33
|
-
constructor({ compilerOptions, cwd,
|
|
32
|
+
constructor({ compilerOptions, cwd, compilers }) {
|
|
34
33
|
this.cwd = cwd;
|
|
35
34
|
this.compilerOptions = {
|
|
36
35
|
...compilerOptions,
|
|
@@ -38,7 +37,6 @@ export class ProjectPrincipal {
|
|
|
38
37
|
allowNonTsExtensions: [...compilers].flat().length > 0,
|
|
39
38
|
};
|
|
40
39
|
const [syncCompilers, asyncCompilers] = compilers;
|
|
41
|
-
this.isReportTypes = report.types || report.nsTypes || report.enumMembers;
|
|
42
40
|
this.extensions = new Set([...DEFAULT_EXTENSIONS, ...syncCompilers.keys(), ...asyncCompilers.keys()]);
|
|
43
41
|
this.syncCompilers = syncCompilers;
|
|
44
42
|
this.asyncCompilers = asyncCompilers;
|
|
@@ -103,11 +101,10 @@ export class ProjectPrincipal {
|
|
|
103
101
|
const sourceFiles = this.getProgramSourceFiles();
|
|
104
102
|
return Array.from(this.projectPaths).filter(filePath => !sourceFiles.has(filePath));
|
|
105
103
|
}
|
|
106
|
-
analyzeSourceFile(filePath) {
|
|
104
|
+
analyzeSourceFile(filePath, { skipTypeOnly }) {
|
|
107
105
|
const sourceFile = this.backend.program?.getSourceFile(filePath);
|
|
108
106
|
if (!sourceFile)
|
|
109
107
|
throw new Error(`Unable to find ${filePath}`);
|
|
110
|
-
const skipTypeOnly = !this.isReportTypes;
|
|
111
108
|
const skipExports = this.skipExportsAnalysis.has(filePath);
|
|
112
109
|
const { imports, exports, scripts } = getImportsAndExports(sourceFile, { skipTypeOnly, skipExports });
|
|
113
110
|
const { internal, unresolved, external } = imports;
|
package/dist/WorkspaceWorker.js
CHANGED
|
@@ -3,7 +3,8 @@ import * as npm from './manifest/index.js';
|
|
|
3
3
|
import * as plugins from './plugins/index.js';
|
|
4
4
|
import { debugLogArray, debugLogObject } from './util/debug.js';
|
|
5
5
|
import { _pureGlob, negate, hasProductionSuffix, hasNoProductionSuffix, prependDirToPattern } from './util/glob.js';
|
|
6
|
-
import {
|
|
6
|
+
import { getKeysByValue } from './util/object.js';
|
|
7
|
+
import { join, toPosix } from './util/path.js';
|
|
7
8
|
const negatedTestFilePatterns = TEST_FILE_PATTERNS.map(negate);
|
|
8
9
|
export class WorkspaceWorker {
|
|
9
10
|
name;
|
|
@@ -52,7 +53,7 @@ export class WorkspaceWorker {
|
|
|
52
53
|
this.enabled[pluginName] = true;
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
|
-
this.enabledPlugins =
|
|
56
|
+
this.enabledPlugins = getKeysByValue(this.enabled, true);
|
|
56
57
|
const enabledPluginNames = this.enabledPlugins.map(name => plugins[name].NAME);
|
|
57
58
|
debugLogObject(`Enabled plugins (${this.name})`, enabledPluginNames);
|
|
58
59
|
}
|
|
@@ -219,7 +220,7 @@ export class WorkspaceWorker {
|
|
|
219
220
|
config: pluginConfig,
|
|
220
221
|
isProduction: this.isProduction,
|
|
221
222
|
});
|
|
222
|
-
dependencies.forEach(specifier => {
|
|
223
|
+
dependencies.map(toPosix).forEach(specifier => {
|
|
223
224
|
pluginDependencies.add(specifier);
|
|
224
225
|
this.referencedDependencies.add([configFilePath, specifier]);
|
|
225
226
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import parseArgs from 'minimist';
|
|
2
2
|
import { compact } from '../../util/array.js';
|
|
3
|
-
import { toBinary, tryResolveFilePaths } from '../util.js';
|
|
3
|
+
import { toBinary, tryResolveFilePath, tryResolveFilePaths } from '../util.js';
|
|
4
4
|
const withPositional = parsed => [parsed._[0], parsed.require].flat();
|
|
5
5
|
const withoutPositional = parsed => [parsed.require].flat();
|
|
6
6
|
const argFilters = {
|
|
7
7
|
'babel-node': withPositional,
|
|
8
8
|
esbuild: withPositional,
|
|
9
|
+
execa: withPositional,
|
|
9
10
|
nodemon: withPositional,
|
|
10
11
|
'ts-node': withPositional,
|
|
11
12
|
zx: withPositional,
|
|
@@ -16,5 +17,6 @@ export const resolve = (binary, args, { cwd }) => {
|
|
|
16
17
|
const parsed = parseArgs(args, { string: ['r'], alias: { require: ['r', 'loader'] }, boolean: ['quiet', 'verbose'] });
|
|
17
18
|
const argFilter = argFilters[binary] ?? argFilters.default;
|
|
18
19
|
const filteredArgs = compact(argFilter(parsed));
|
|
19
|
-
|
|
20
|
+
const bin = binary.startsWith('.') ? tryResolveFilePath(cwd, binary, binary) : [toBinary(binary)];
|
|
21
|
+
return [...bin, ...tryResolveFilePaths(cwd, filteredArgs)];
|
|
20
22
|
};
|
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ const printReport = reporter in reporters ? reporters[reporter] : await _load(re
|
|
|
23
23
|
const run = async () => {
|
|
24
24
|
try {
|
|
25
25
|
const perfObserver = new Performance(isObservePerf);
|
|
26
|
-
const { report, issues, counters } = await main({
|
|
26
|
+
const { report, issues, counters, rules } = await main({
|
|
27
27
|
cwd,
|
|
28
28
|
tsConfigFile: tsConfig,
|
|
29
29
|
gitignore: !isNoGitIgnore,
|
|
@@ -33,7 +33,7 @@ const run = async () => {
|
|
|
33
33
|
});
|
|
34
34
|
await printReport({ report, issues, cwd, isProduction, options: reporterOptions });
|
|
35
35
|
const totalErrorCount = Object.keys(report)
|
|
36
|
-
.filter(reportGroup => report[reportGroup])
|
|
36
|
+
.filter(reportGroup => report[reportGroup] && rules[reportGroup] === 'error')
|
|
37
37
|
.reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
|
|
38
38
|
if (isObservePerf) {
|
|
39
39
|
await perfObserver.finalize();
|
package/dist/constants.js
CHANGED
|
@@ -2,9 +2,8 @@ export const ROOT_WORKSPACE_NAME = '.';
|
|
|
2
2
|
export const KNIP_CONFIG_LOCATIONS = ['knip.json', 'knip.jsonc', '.knip.json', '.knip.jsonc', 'knip.ts', 'knip.js'];
|
|
3
3
|
export const DEFAULT_EXTENSIONS = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.mts', '.cts'];
|
|
4
4
|
export const TEST_FILE_PATTERNS = [
|
|
5
|
-
'
|
|
6
|
-
'**/__tests__/**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
7
|
-
'**/test/**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
5
|
+
'**/*{.,-}{test,spec}.{js,jsx,ts,tsx,mjs,cjs}',
|
|
6
|
+
'**/{test,__tests__}/**/*.{js,jsx,ts,tsx,mjs,cjs}',
|
|
8
7
|
];
|
|
9
8
|
export const IGNORED_GLOBAL_BINARIES = [
|
|
10
9
|
'bun',
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { LoaderError } from './util/errors.js';
|
|
|
11
11
|
import { findFile } from './util/fs.js';
|
|
12
12
|
import { _glob } from './util/glob.js';
|
|
13
13
|
import { getEntryPathFromManifest, getPackageNameFromFilePath, getPackageNameFromModuleSpecifier, } from './util/modules.js';
|
|
14
|
-
import { dirname, isInNodeModules, join, isInternal,
|
|
14
|
+
import { dirname, isInNodeModules, join, isInternal, toAbsolute } from './util/path.js';
|
|
15
15
|
import { _resolveSpecifier, _tryResolve } from './util/require.js';
|
|
16
16
|
import { _require } from './util/require.js';
|
|
17
17
|
import { loadTSConfig as loadCompilerOptions } from './util/tsconfig-loader.js';
|
|
@@ -22,17 +22,22 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
22
22
|
const chief = new ConfigurationChief({ cwd, isProduction });
|
|
23
23
|
const deputy = new DependencyDeputy({ isStrict });
|
|
24
24
|
const factory = new PrincipalFactory();
|
|
25
|
-
const collector = new IssueCollector({ cwd });
|
|
26
25
|
const streamer = new ConsoleStreamer({ isEnabled: isShowProgress });
|
|
27
26
|
streamer.cast('Reading workspace configuration(s)...');
|
|
28
27
|
await chief.init();
|
|
29
28
|
const compilers = chief.getCompilers();
|
|
30
29
|
const workspaces = chief.getEnabledWorkspaces();
|
|
31
30
|
const report = chief.getIssueTypesToReport();
|
|
31
|
+
const rules = chief.getRules();
|
|
32
|
+
const isReportDependencies = report.dependencies || report.unlisted || report.unresolved;
|
|
33
|
+
const isReportValues = report.exports || report.nsExports || report.classMembers;
|
|
34
|
+
const isReportTypes = report.types || report.nsTypes || report.enumMembers;
|
|
35
|
+
const collector = new IssueCollector({ cwd, rules });
|
|
36
|
+
const enabledPluginsStore = new Map();
|
|
32
37
|
debugLogObject('Included workspaces', workspaces);
|
|
33
38
|
const handleReferencedDependency = ({ specifier, containingFilePath, principal, workspace, }) => {
|
|
34
39
|
if (isInternal(specifier)) {
|
|
35
|
-
const absSpecifier =
|
|
40
|
+
const absSpecifier = toAbsolute(specifier, dirname(containingFilePath));
|
|
36
41
|
const filePath = _tryResolve(absSpecifier, containingFilePath);
|
|
37
42
|
if (filePath) {
|
|
38
43
|
principal.addEntryPath(filePath);
|
|
@@ -68,7 +73,6 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
75
|
};
|
|
71
|
-
const enabledPluginsStore = new Map();
|
|
72
76
|
for (const workspace of workspaces) {
|
|
73
77
|
const { name, dir, config, ancestors } = workspace;
|
|
74
78
|
const { paths, ignoreDependencies, ignoreBinaries } = config;
|
|
@@ -80,7 +84,7 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
80
84
|
throw new LoaderError(`Unable to load package.json for ${name}`);
|
|
81
85
|
deputy.addWorkspace({ name, dir, manifestPath, manifest, ignoreDependencies, ignoreBinaries });
|
|
82
86
|
const compilerOptions = await loadCompilerOptions(join(dir, tsConfigFile ?? 'tsconfig.json'));
|
|
83
|
-
const principal = factory.getPrincipal({ cwd: dir,
|
|
87
|
+
const principal = factory.getPrincipal({ cwd: dir, paths, compilerOptions, compilers });
|
|
84
88
|
const worker = new WorkspaceWorker({
|
|
85
89
|
name,
|
|
86
90
|
dir,
|
|
@@ -173,10 +177,9 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
173
177
|
const exportedSymbols = new Map();
|
|
174
178
|
const importedSymbols = new Map();
|
|
175
179
|
const analyzeSourceFile = (filePath) => {
|
|
176
|
-
collector.counters.processed++;
|
|
177
180
|
const workspace = chief.findWorkspaceByFilePath(filePath);
|
|
178
181
|
if (workspace) {
|
|
179
|
-
const { imports, exports, scripts } = principal.analyzeSourceFile(filePath);
|
|
182
|
+
const { imports, exports, scripts } = principal.analyzeSourceFile(filePath, { skipTypeOnly: !isReportTypes });
|
|
180
183
|
const { internal, external, unresolved } = imports;
|
|
181
184
|
const { exported, duplicate } = exports;
|
|
182
185
|
if (exported.size > 0)
|
|
@@ -246,51 +249,53 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
246
249
|
} while (size !== principal.entryPaths.size);
|
|
247
250
|
const unusedFiles = principal.getUnreferencedFiles();
|
|
248
251
|
collector.addFilesIssues(unusedFiles);
|
|
249
|
-
collector.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
252
|
+
collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });
|
|
253
|
+
if (isReportValues || isReportTypes) {
|
|
254
|
+
streamer.cast('Analyzing source files...');
|
|
255
|
+
for (const [filePath, exportItems] of exportedSymbols.entries()) {
|
|
256
|
+
const importedModule = importedSymbols.get(filePath);
|
|
257
|
+
if (importedModule) {
|
|
258
|
+
for (const [symbol, exportedItem] of exportItems.entries()) {
|
|
259
|
+
if (principal.isPublicExport(exportedItem))
|
|
260
|
+
continue;
|
|
261
|
+
if (importedModule.symbols.has(symbol)) {
|
|
262
|
+
if (report.enumMembers && exportedItem.type === 'enum' && exportedItem.members) {
|
|
263
|
+
principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
|
|
264
|
+
collector.addIssue({ type: 'enumMembers', filePath, symbol: member, parentSymbol: symbol });
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (report.classMembers && exportedItem.type === 'class' && exportedItem.members) {
|
|
268
|
+
principal.findUnusedMembers(filePath, exportedItem.members).forEach(member => {
|
|
269
|
+
collector.addIssue({ type: 'classMembers', filePath, symbol: member, parentSymbol: symbol });
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
continue;
|
|
262
273
|
}
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
274
|
+
if (importedModule.isReExported || importedModule.isStar) {
|
|
275
|
+
const isReExportedByEntryFile = isExportedInEntryFile(importedModule);
|
|
276
|
+
if (!isReExportedByEntryFile && !principal.hasExternalReferences(filePath, exportedItem)) {
|
|
277
|
+
if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
|
|
278
|
+
collector.addIssue({ type: 'nsTypes', filePath, symbol, symbolType: exportedItem.type });
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
collector.addIssue({ type: 'nsExports', filePath, symbol });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
267
284
|
}
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
if (importedModule.isReExported || importedModule.isStar) {
|
|
271
|
-
const isReExportedByEntryFile = isExportedInEntryFile(importedModule);
|
|
272
|
-
if (!isReExportedByEntryFile && !principal.hasExternalReferences(filePath, exportedItem)) {
|
|
285
|
+
else {
|
|
273
286
|
if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
|
|
274
|
-
collector.addIssue({ type: '
|
|
287
|
+
collector.addIssue({ type: 'types', filePath, symbol, symbolType: exportedItem.type });
|
|
275
288
|
}
|
|
276
|
-
else {
|
|
277
|
-
collector.addIssue({ type: '
|
|
289
|
+
else if (!importedModule.isDynamic || !principal.hasExternalReferences(filePath, exportedItem)) {
|
|
290
|
+
collector.addIssue({ type: 'exports', filePath, symbol });
|
|
278
291
|
}
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
|
-
else {
|
|
282
|
-
if (['enum', 'type', 'interface'].includes(exportedItem.type)) {
|
|
283
|
-
collector.addIssue({ type: 'types', filePath, symbol, symbolType: exportedItem.type });
|
|
284
|
-
}
|
|
285
|
-
else if (!importedModule.isDynamic || !principal.hasExternalReferences(filePath, exportedItem)) {
|
|
286
|
-
collector.addIssue({ type: 'exports', filePath, symbol });
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
294
|
}
|
|
290
295
|
}
|
|
291
296
|
}
|
|
292
297
|
}
|
|
293
|
-
if (
|
|
298
|
+
if (isReportDependencies) {
|
|
294
299
|
const { dependencyIssues, devDependencyIssues } = deputy.settleDependencyIssues();
|
|
295
300
|
dependencyIssues.forEach(issue => collector.addIssue(issue));
|
|
296
301
|
if (!isProduction)
|
|
@@ -298,5 +303,5 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
298
303
|
}
|
|
299
304
|
const { issues, counters } = collector.getIssues();
|
|
300
305
|
streamer.clear();
|
|
301
|
-
return { report, issues, counters };
|
|
306
|
+
return { report, issues, counters, rules };
|
|
302
307
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { compact } from '../../util/array.js';
|
|
2
2
|
import { getPackageNameFromModuleSpecifier } from '../../util/modules.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isInternal, dirname, toAbsolute } from '../../util/path.js';
|
|
4
4
|
import { load } from '../../util/plugin.js';
|
|
5
5
|
import { _resolve } from '../../util/require.js';
|
|
6
6
|
import { fallback } from './fallback.js';
|
|
@@ -36,7 +36,7 @@ export const getDependenciesDeep = async (configFilePath, dependencies = new Set
|
|
|
36
36
|
if (config.extends) {
|
|
37
37
|
for (const extend of [config.extends].flat()) {
|
|
38
38
|
if (isInternal(extend)) {
|
|
39
|
-
const filePath =
|
|
39
|
+
const filePath = toAbsolute(extend, dirname(configFilePath));
|
|
40
40
|
const extendConfigFilePath = _resolve(filePath);
|
|
41
41
|
dependencies.add(extendConfigFilePath);
|
|
42
42
|
addAll(await getDependenciesDeep(extendConfigFilePath, dependencies, options));
|
|
@@ -82,7 +82,7 @@ const getImportPluginDependencies = (settings) => {
|
|
|
82
82
|
};
|
|
83
83
|
export const getDependenciesFromSettings = (settings = {}) => {
|
|
84
84
|
return compact(Object.entries(settings).reduce((packageNames, [settingKey, settings]) => {
|
|
85
|
-
if (/^import\/(parsers|resolvers)?/.test(settingKey)) {
|
|
85
|
+
if (/^import\/(parsers|resolvers)?/.test(settingKey) && typeof settings !== 'string') {
|
|
86
86
|
return [...packageNames, ...getImportPluginDependencies(settings)];
|
|
87
87
|
}
|
|
88
88
|
return packageNames;
|
|
@@ -4,7 +4,7 @@ type ParserOptions = {
|
|
|
4
4
|
presets: string[];
|
|
5
5
|
};
|
|
6
6
|
};
|
|
7
|
-
type Settings = Record<string, Record<string, unknown
|
|
7
|
+
type Settings = Record<string, Record<string, unknown> | string>;
|
|
8
8
|
type Rules = Record<string, string | number>;
|
|
9
9
|
type BaseConfig = {
|
|
10
10
|
extends?: string | string[];
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { join, isInternal, toAbsolute, dirname } from '../../util/path.js';
|
|
2
2
|
import { timerify } from '../../util/Performance.js';
|
|
3
3
|
import { hasDependency, load } from '../../util/plugin.js';
|
|
4
4
|
export const NAME = 'Jest';
|
|
5
5
|
export const ENABLERS = ['jest'];
|
|
6
6
|
export const isEnabled = ({ dependencies, manifest }) => hasDependency(dependencies, ENABLERS) || Boolean(manifest.name?.startsWith('jest-presets'));
|
|
7
7
|
export const CONFIG_FILE_PATTERNS = ['jest.config.{js,ts,mjs,cjs,json}', 'package.json'];
|
|
8
|
-
const maybeJoin = (base, id) => (isAbsolute(id) ? id : join(dirname(base), id));
|
|
9
8
|
const resolveExtensibleConfig = async (configFilePath) => {
|
|
10
9
|
const config = await load(configFilePath);
|
|
11
10
|
if (config?.preset) {
|
|
12
11
|
const { preset } = config;
|
|
13
12
|
if (isInternal(preset)) {
|
|
14
|
-
const presetConfigPath =
|
|
13
|
+
const presetConfigPath = toAbsolute(preset, dirname(configFilePath));
|
|
15
14
|
const presetConfig = await resolveExtensibleConfig(presetConfigPath);
|
|
16
15
|
Object.assign(config, presetConfig);
|
|
17
16
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { OwnershipEngine } from '@snyk/github-codeowners/dist/lib/ownership/index.js';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import {
|
|
3
|
+
import { toRelative, relative, resolve } from '../util/path.js';
|
|
4
4
|
import { getTitle, logTitle, logIssueLine } from './util.js';
|
|
5
5
|
const logIssueSet = (issues) => {
|
|
6
6
|
issues
|
|
7
7
|
.sort((a, b) => (a.owner < b.owner ? -1 : 1))
|
|
8
|
-
.forEach(issue => console.log(chalk.cyan(issue.owner),
|
|
8
|
+
.forEach(issue => console.log(chalk.cyan(issue.owner), toRelative(issue.symbol)));
|
|
9
9
|
};
|
|
10
10
|
const logIssueRecord = (issues) => {
|
|
11
11
|
const sortedByFilePath = issues.sort((a, b) => (a.owner < b.owner ? -1 : 1));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getTitle, logTitle, logIssueLine, logIssueSet } from './util.js';
|
|
2
2
|
const logIssueRecord = (issues) => {
|
|
3
3
|
const sortedByFilePath = issues.sort((a, b) => (a.filePath > b.filePath ? 1 : -1));
|
|
4
|
-
sortedByFilePath.forEach(
|
|
4
|
+
sortedByFilePath.forEach(logIssueLine);
|
|
5
5
|
};
|
|
6
6
|
export default ({ report, issues }) => {
|
|
7
7
|
const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
1
2
|
import EasyTable from 'easy-table';
|
|
2
3
|
import { relative } from '../util/path.js';
|
|
3
|
-
import { getTitle, logTitle, logIssueSet } from './util.js';
|
|
4
|
+
import { getTitle, logTitle, logIssueSet, identity } from './util.js';
|
|
4
5
|
const TRUNCATE_WIDTH = 40;
|
|
5
6
|
const truncate = (text) => (text.length > TRUNCATE_WIDTH ? text.slice(0, TRUNCATE_WIDTH - 3) + '...' : text);
|
|
6
7
|
const logIssueRecord = (issues) => {
|
|
7
8
|
const table = new EasyTable();
|
|
8
9
|
issues.forEach(issue => {
|
|
9
|
-
|
|
10
|
-
issue.
|
|
11
|
-
issue.
|
|
12
|
-
table.cell('
|
|
10
|
+
const print = issue.severity === 'warn' ? chalk.grey : identity;
|
|
11
|
+
table.cell('symbol', print(issue.symbols ? truncate(issue.symbols.join(', ')) : issue.symbol));
|
|
12
|
+
issue.parentSymbol && table.cell('parentSymbol', print(issue.parentSymbol));
|
|
13
|
+
issue.symbolType && table.cell('symbolType', print(issue.symbolType));
|
|
14
|
+
table.cell('filePath', print(relative(issue.filePath)));
|
|
13
15
|
table.newRow();
|
|
14
16
|
});
|
|
15
17
|
console.log(table.sort(['filePath', 'parentSymbol', 'symbol']).print().trim());
|
package/dist/reporters/util.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ISSUE_TYPE_TITLE } from '../constants.js';
|
|
2
|
+
import { IssueSeverity } from '../types/issues.js';
|
|
3
|
+
export declare const identity: (text: string) => string;
|
|
2
4
|
export declare const getTitle: (reportType: keyof typeof ISSUE_TYPE_TITLE) => string;
|
|
3
5
|
export declare const logTitle: (title: string, count: number) => void;
|
|
4
6
|
type LogIssueLine = {
|
|
@@ -6,7 +8,8 @@ type LogIssueLine = {
|
|
|
6
8
|
filePath: string;
|
|
7
9
|
symbols?: string[];
|
|
8
10
|
parentSymbol?: string;
|
|
11
|
+
severity?: IssueSeverity;
|
|
9
12
|
};
|
|
10
|
-
export declare const logIssueLine: ({ owner, filePath, symbols, parentSymbol }: LogIssueLine) => void;
|
|
13
|
+
export declare const logIssueLine: ({ owner, filePath, symbols, parentSymbol, severity }: LogIssueLine) => void;
|
|
11
14
|
export declare const logIssueSet: (issues: string[]) => void;
|
|
12
15
|
export {};
|
package/dist/reporters/util.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { ISSUE_TYPE_TITLE } from '../constants.js';
|
|
3
|
-
import {
|
|
3
|
+
import { toRelative, relative } from '../util/path.js';
|
|
4
|
+
export const identity = (text) => text;
|
|
4
5
|
export const getTitle = (reportType) => {
|
|
5
6
|
return ISSUE_TYPE_TITLE[reportType];
|
|
6
7
|
};
|
|
7
8
|
export const logTitle = (title, count) => console.log(`${chalk.bold.yellow.underline(title)} (${count})`);
|
|
8
|
-
export const logIssueLine = ({ owner, filePath, symbols, parentSymbol }) => {
|
|
9
|
+
export const logIssueLine = ({ owner, filePath, symbols, parentSymbol, severity }) => {
|
|
9
10
|
const symbol = symbols ? `: ${symbols.join(', ')}` : '';
|
|
10
11
|
const parent = parentSymbol ? ` (${parentSymbol})` : '';
|
|
11
|
-
|
|
12
|
+
const print = severity === 'warn' ? chalk.grey : identity;
|
|
13
|
+
console.log(`${owner ? `${chalk.cyan(owner)} ` : ''}${print(`${relative(filePath)}${symbol}${parent}`)}`);
|
|
12
14
|
};
|
|
13
15
|
export const logIssueSet = (issues) => {
|
|
14
|
-
issues.sort().forEach(value => console.log(
|
|
16
|
+
issues.sort().forEach(value => console.log(toRelative(value)));
|
|
15
17
|
};
|
package/dist/types/config.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ConfigurationValidator } from '../ConfigurationValidator.js';
|
|
3
3
|
import * as Plugins from '../plugins/index.js';
|
|
4
|
+
import { Rules } from './issues.js';
|
|
4
5
|
import type { SyncCompilers, AsyncCompilers } from '../types/compilers.js';
|
|
5
6
|
export type RawConfiguration = z.infer<typeof ConfigurationValidator>;
|
|
6
7
|
type NormalizedGlob = string[];
|
|
@@ -22,6 +23,7 @@ interface BaseWorkspaceConfiguration {
|
|
|
22
23
|
export interface WorkspaceConfiguration extends BaseWorkspaceConfiguration, Partial<PluginsConfiguration> {
|
|
23
24
|
}
|
|
24
25
|
export interface Configuration {
|
|
26
|
+
rules: Rules;
|
|
25
27
|
include: string[];
|
|
26
28
|
exclude: string[];
|
|
27
29
|
ignore: NormalizedGlob;
|