core-quality-unused-import
Pack: core-quality Default severity: MAJOR Languages: TypeScript, JavaScript Lifecycle: experimental Confidence: 0.9
What it catches
Import bindings that are never referenced in the file:
import Foo from 'x'— default binding flagged when unused.import * as Foo from 'x'— namespace binding flagged when unused.import { A, B } from 'x'— each named binding checked independently; one finding per unused name.
Side-effect imports (import 'x') have no binding and are never flagged.
Why this matters for vibe-coded apps
Every unused import is:
- Bundle bloat — most bundlers tree-shake well, but
import * asdefeats it, and CJS-shape modules drag in side effects. - Supply-chain surface — a CVE in
lodaashonly matters because someone importedlodaash. Slopsquatting attacks ride this exact lever. - Misleading to agents — the next AI looking at the file may believe
useStateis in use somewhere it isn't, and propose changes that depend on that ghost.
Example — flagged
import { useState } from 'react'; // ← flag (named)
import lodash from 'lodash'; // ← flag (default)
import * as helpers from './helpers'; // ← flag (namespace)
import { partial, unused } from './partial'; // ← flag `unused` only
export function take(): unknown {
return partial();
}Example — not flagged
import { useState } from 'react';
export function counter(): number {
const [n, setN] = useState(0);
setN(n + 1);
return n;
}
// Type-only usage counts as a use.
import type { Result } from './result';
export function ok<T>(value: T): Result<T> { … }
// Renamed binding — local alias is what's used.
import { compute as compute2 } from './compute';
export function go(): number { return compute2(); }
// Side-effect-only import: no binding, never flagged.
import './styles.css';Suggested fix
- Named import is unused — drop just that name from the brace list. Don't delete the whole line if other names are still in use.
- Default / namespace import is unused — delete the whole import line.
- Import was kept for side effects — convert to side-effect form:
import 'foo';.
Suppressing
// Reason: re-exported via barrel even though this file does not reference it.
// codemore-ignore-next-line: core-quality-unused-import
import { kept } from './kept';
export { kept };The directive must be on the line immediately before the target. If you put a comment between them, the directive suppresses the comment instead.
Implementation
AST-based. Reuses the same identifier-reference pre-pass as core-quality-unused-variable. Then walks every ImportDeclaration: checks the default name, each named-import specifier, and any namespace import independently against the reference set, emitting one hit per unused binding. Side-effect-only imports (no importClause) are skipped by construction.
Source: `shared/packs/core-quality/core-quality-unused-import.ts` Fixtures: `corpus/rules/core-quality-unused-import/`