Lines 167-207javascript
167 severity: dev ? "info" : "low",
168 confidence: dev || ecosystem ? "low" : "medium",
169 title: `Unused ${section === "dependencies" ? "dependency" : section.replace(/ies$/, "y")}: ${name}`,
170 reason: "No recorded package import, package-script command, recognized config mention, framework peer contract, or implicit style-compiler input uses this declaration.",
171 detail: `"${name}" is declared in ${section} but never imported in source, never referenced by a script, and not mentioned in any known config file. Dynamic/config-convention usage can't be fully ruled out — review before removing.`,
174 fixHint: `npm uninstall ${name} (after confirming no config/CLI usage)`,
180 // ---- missing (phantom) dependencies: imported but declared nowhere ----
181 for (const [name, use] of usedPackages) {
182 if (allDeclared.has(name) || name === selfName || workspacePkgNames.has(name)) continue;
183 const files = [...use.files];
184 if (files.every(isNonRuntimeFile) || isGeneratedNapiPackage(name, files)) continue;
185 const testOnly = files.every((f) => /(^|[/\\])(test|tests|__tests__|spec|e2e|__mocks__)([/\\]|$)|[._-](test|spec)\.[a-z]+$/i.test(f));
186 const stylesheetOnly = [...use.specs].length > 0 && [...use.specs].every(isStylesheetSpecifier);
187 const usageReason = stylesheetOnly
188 ? `Direct stylesheet import(s) resolve through "${name}"; CSS-only imports are build/runtime inputs even without JavaScript bindings.`
189 : use.typeOnly
MediumDynamic Require
Package source references dynamic require/import behavior.
src/analysis/dep-check.jsView on unpkg · L187 190 ? `Direct type-only import(s) resolve through "${name}"; the compiler still requires a declared package.`
191 : `Direct source import(s) resolve through "${name}", but the nearest package manifest does not declare it.`;
192 findings.push(makeFinding({
195 severity: testOnly ? "low" : "medium",
197 title: `Missing dependency: ${name}`,
199 detail: `"${name}" is imported by ${files.length} file(s) in scope ${meta.scope} but not declared in ${manifest} — it only works via a transitive install (phantom dependency) and can break on any lockfile change.`,
202 line: use.lines.get(files[0]) || 0,
203 evidence: files.slice(0, 5).map((f) => ({ file: f, line: use.lines.get(f) || 0, snippet: "" })),
205 fixHint: `npm install ${testOnly ? "--save-dev " : ""}${name}`,