knip
Version:
Find unused files, dependencies and exports in your TypeScript and JavaScript projects
100 lines (99 loc) • 4.96 kB
JavaScript
import EasyTable from 'easy-table';
import picocolors from 'picocolors';
import { ROOT_WORKSPACE_NAME } from '../constants.js';
import { relative, toRelative } from '../util/path.js';
import { getTitle, identity, logTitle } from './util.js';
const dim = picocolors.gray;
const bright = picocolors.whiteBright;
const TRUNCATE_WIDTH = 40;
const truncate = (text) => (text.length > TRUNCATE_WIDTH ? `${text.slice(0, TRUNCATE_WIDTH - 3)}...` : text);
const truncateStart = (text, width) => (text.length > width ? `...${text.slice(-(width - 3))}` : text);
const sortByPos = (a, b) => {
const [f, r, c] = a.filePath.split(':');
const [f2, r2, c2] = b.filePath.split(':');
return f === f2 ? (Number(r) === Number(r2) ? Number(c) - Number(c2) : Number(r) - Number(r2)) : f.localeCompare(f2);
};
const hl = (issue) => {
if (issue.specifier && issue.specifier !== issue.symbol && issue.specifier.includes(issue.symbol)) {
const parts = issue.specifier.split(issue.symbol);
const right = parts.slice(1).join('');
const max = TRUNCATE_WIDTH - issue.symbol.length - right.length;
const part = parts[0];
const left = part.length > 3 ? (max <= 3 ? `...${part.slice(-3)}` : truncateStart(part, max)) : part;
return [dim(left), bright(issue.symbol), dim(right)].join('');
}
return issue.symbol;
};
const logIssueRecord = (issues) => {
const table = new EasyTable();
for (const issue of issues) {
const print = issue.isFixed || issue.severity === 'warn' ? dim : identity;
table.cell('symbol', print(issue.symbols ? truncate(issue.symbols.map(s => s.symbol).join(', ')) : hl(issue)));
issue.parentSymbol && table.cell('parentSymbol', print(issue.parentSymbol));
issue.symbolType && table.cell('symbolType', print(issue.symbolType));
const pos = issue.line === undefined ? '' : `:${issue.line}${issue.col === undefined ? '' : `:${issue.col}`}`;
const cell = `${relative(issue.filePath)}${pos}`;
table.cell('filePath', print(cell));
issue.isFixed && table.cell('fixed', print('(removed)'));
table.newRow();
}
console.log(table.sort(sortByPos).print().trim());
};
export default ({ report, issues, tagHints, configurationHints, noConfigHints, isShowProgress }) => {
const reportMultipleGroups = Object.values(report).filter(Boolean).length > 1;
let totalIssues = 0;
for (const [reportType, isReportType] of Object.entries(report)) {
if (reportType === '_files')
continue;
if (isReportType) {
const title = reportMultipleGroups && getTitle(reportType);
if (reportType === 'files') {
const issuesForType = Array.from(issues._files);
if (issuesForType.length > 0) {
title && logTitle(title, issuesForType.length);
const sortedIssues = issuesForType.sort((a, b) => a.filePath.localeCompare(b.filePath));
for (const issue of sortedIssues) {
const relPath = toRelative(issue.filePath);
if (issue.isFixed)
console.log(dim(`${relPath} (removed)`));
else if (issue.severity === 'warn')
console.log(dim(relPath));
else
console.log(relPath);
}
totalIssues = totalIssues + issuesForType.length;
}
}
else {
const issuesForType = Object.values(issues[reportType]).flatMap(Object.values);
if (issuesForType.length > 0) {
title && logTitle(title, issuesForType.length);
logIssueRecord(issuesForType);
totalIssues = totalIssues + issuesForType.length;
}
}
}
}
if (!noConfigHints) {
if (configurationHints.size > 0) {
logTitle('Configuration hints', configurationHints.size);
for (const hint of configurationHints) {
const { type, workspaceName, identifier } = hint;
const message = `Unused item in ${type}`;
const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : '';
console.warn(dim(`${message}${workspace}:`), identifier);
}
}
if (tagHints.size > 0) {
logTitle('Tag issues', tagHints.size);
for (const hint of tagHints) {
const { filePath, identifier, tagName } = hint;
const message = `Unused tag in ${toRelative(filePath)}:`;
console.warn(dim(message), `${identifier} → ${tagName}`);
}
}
}
if (totalIssues === 0 && isShowProgress) {
console.log('✂️ Excellent, Knip found no issues.');
}
};