knip
Version:
Find unused files, dependencies and exports in your TypeScript and JavaScript projects
88 lines (87 loc) • 3.15 kB
JavaScript
import picomatch from 'picomatch';
import { initCounters, initIssues } from './issues/initializers.js';
import { timerify } from './util/Performance.js';
import { relative } from './util/path.js';
const hasConfigurationHint = (hints, hint) => Array.from(hints).some(item => item.identifier === hint.identifier && item.type === hint.type && item.workspaceName === hint.workspaceName);
const isMatch = timerify(picomatch.isMatch, 'isMatch');
export class IssueCollector {
cwd;
rules;
filters;
issues = initIssues();
counters = initCounters();
referencedFiles = new Set();
configurationHints = new Set();
tagHints = new Set();
ignorePatterns = new Set();
isMatch;
constructor({ cwd, rules, filters }) {
this.cwd = cwd;
this.rules = rules;
this.filters = filters;
this.isMatch = () => false;
}
addIgnorePatterns(patterns) {
for (const pattern of patterns)
this.ignorePatterns.add(pattern);
const p = [...this.ignorePatterns];
this.isMatch = (filePath) => isMatch(filePath, p, { dot: true });
}
addFileCounts({ processed, unused }) {
this.counters.processed += processed;
this.counters.total += processed + unused;
}
addFilesIssues(filePaths) {
for (const filePath of filePaths) {
if (this.filters.dir && !filePath.startsWith(`${this.filters.dir}/`))
continue;
if (this.referencedFiles.has(filePath))
continue;
if (this.isMatch(filePath))
continue;
this.issues.files.add(filePath);
this.issues._files.add({ type: 'files', filePath, symbol: relative(filePath), severity: this.rules.files });
this.counters.files++;
this.counters.processed++;
}
}
addIssue(issue) {
if (this.filters.dir && !issue.filePath.startsWith(`${this.filters.dir}/`))
return;
if (this.isMatch(issue.filePath))
return;
const key = relative(this.cwd, issue.filePath);
const { type } = issue;
issue.severity = this.rules[type];
const issues = this.issues[type];
issues[key] = issues[key] ?? {};
const symbol = type.endsWith('Members') && issue.parentSymbol ? `${issue.parentSymbol}.${issue.symbol}` : issue.symbol;
if (!issues[key][symbol]) {
issues[key][symbol] = issue;
this.counters[issue.type]++;
}
return issue;
}
addConfigurationHint(issue) {
if (!hasConfigurationHint(this.configurationHints, issue)) {
this.configurationHints.add(issue);
}
}
addTagHint(issue) {
this.tagHints.add(issue);
}
purge() {
const unusedFiles = this.issues.files;
this.issues = initIssues();
this.counters = initCounters();
return unusedFiles;
}
getIssues() {
return {
issues: this.issues,
counters: this.counters,
tagHints: this.tagHints,
configurationHints: this.configurationHints,
};
}
}