diff --git a/packages/knip/src/index.ts b/packages/knip/src/index.ts index e5dda3a36..8378492ee 100644 --- a/packages/knip/src/index.ts +++ b/packages/knip/src/index.ts @@ -582,7 +582,14 @@ export const main = async (unresolvedConfiguration: CommandLineOptions) => { for (const specifier of file.imports.external) { const packageName = getPackageNameFromModuleSpecifier(specifier); const isHandled = packageName && deputy.maybeAddReferencedExternalDependency(ws, packageName); - if (!isHandled) collector.addIssue({ type: 'unlisted', filePath, workspace: ws.name, symbol: specifier }); + if (!isHandled) + collector.addIssue({ + type: 'unlisted', + filePath, + workspace: ws.name, + symbol: packageName ?? specifier, + specifier, + }); } } diff --git a/packages/knip/src/reporters/symbols.ts b/packages/knip/src/reporters/symbols.ts index ec47cd062..51b637838 100644 --- a/packages/knip/src/reporters/symbols.ts +++ b/packages/knip/src/reporters/symbols.ts @@ -6,14 +6,30 @@ import type { Issue, ReporterOptions } from '../types/issues.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: string) => (text.length > TRUNCATE_WIDTH ? `${text.slice(0, TRUNCATE_WIDTH - 3)}...` : text); +const hl = (issue: Issue) => { + if (issue.specifier) { + const [specifier, highlight] = issue.specifier; + if (highlight && specifier.includes(highlight)) { + const parts = specifier.split(highlight); + const rest = parts.slice(1).join(''); + return [dim(parts[0]), bright(highlight), dim(rest)].join(''); + } + return specifier; + } + return issue.symbol; +}; + const logIssueRecord = (issues: Issue[]) => { const table = new EasyTable(); for (const issue of issues) { - const print = issue.isFixed || issue.severity === 'warn' ? picocolors.gray : identity; - table.cell('symbol', print(issue.symbols ? truncate(issue.symbols.map(s => s.symbol).join(', ')) : issue.symbol)); + 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}`}`; @@ -41,8 +57,8 @@ export default ({ report, issues, tagHints, configurationHints, noConfigHints, i title && logTitle(title, issuesForType.length); for (const issue of issuesForType) { const relPath = toRelative(issue.filePath); - if (issue.isFixed) console.log(picocolors.gray(`${relPath} (removed)`)); - else if (issue.severity === 'warn') console.log(picocolors.gray(relPath)); + 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; @@ -66,7 +82,7 @@ export default ({ report, issues, tagHints, configurationHints, noConfigHints, i const message = `Unused item in ${type}`; const workspace = workspaceName && workspaceName !== ROOT_WORKSPACE_NAME ? ` (workspace: ${workspaceName})` : ''; - console.warn(picocolors.gray(`${message}${workspace}:`), identifier); + console.warn(dim(`${message}${workspace}:`), identifier); } } if (tagHints.size > 0) { @@ -74,7 +90,7 @@ export default ({ report, issues, tagHints, configurationHints, noConfigHints, i for (const hint of tagHints) { const { filePath, identifier, tagName } = hint; const message = `Unused tag in ${toRelative(filePath)}:`; - console.warn(picocolors.gray(message), `${identifier} → ${tagName}`); + console.warn(dim(message), `${identifier} → ${tagName}`); } } } diff --git a/packages/knip/src/types/issues.ts b/packages/knip/src/types/issues.ts index 215035995..ef1d42cc6 100644 --- a/packages/knip/src/types/issues.ts +++ b/packages/knip/src/types/issues.ts @@ -18,6 +18,7 @@ export type Issue = { symbols?: IssueSymbol[]; symbolType?: SymbolType; parentSymbol?: string; + specifier?: string; severity?: IssueSeverity; pos?: number; line?: number; diff --git a/packages/knip/src/util/get-referenced-inputs.ts b/packages/knip/src/util/get-referenced-inputs.ts index fdad84476..66609a273 100644 --- a/packages/knip/src/util/get-referenced-inputs.ts +++ b/packages/knip/src/util/get-referenced-inputs.ts @@ -43,6 +43,7 @@ export const getReferencedInputsHandler = filePath: containingFilePath, workspace: workspace.name, symbol: binaryName, + specifier, }); return; } @@ -65,7 +66,8 @@ export const getReferencedInputsHandler = type: 'unlisted', filePath: containingFilePath, workspace: specifierWorkspace.name, - symbol: specifier, + symbol: packageName ?? specifier, + specifier, }); } return; @@ -98,6 +100,7 @@ export const getReferencedInputsHandler = filePath: containingFilePath, workspace: workspace.name, symbol: packageName ?? specifier, + specifier, }); } else if (!isGitIgnored(filePath)) { // Let's start out conservatively @@ -106,7 +109,8 @@ export const getReferencedInputsHandler = type: 'unresolved', filePath: containingFilePath, workspace: workspace.name, - symbol: specifier, + symbol: packageName ?? specifier, + specifier, }); } else { debugLog(workspace.name, `Unable to resolve ${toDebugString(input)}`);