@@ -58,7 +58,7 @@ import { renderOverrideFindings } from "./output/override-findings-terminal.js";
5858import { installSkill } from "./skills/install.js" ;
5959import { readConfig , validateCaCertFile } from "./cli/config.js" ;
6060import { runConfigCommand } from "./cli/config-command.js" ;
61- import { readDirectDependencyNames } from "./utils/package-json.js" ;
61+ import { readDirectDependencyNames , readOverridesAndResolutions } from "./utils/package-json.js" ;
6262import {
6363 applyFixesIfRequested ,
6464 FixExecutionResult ,
@@ -645,6 +645,20 @@ if (parsedArgs) {
645645 if ( options . checkOverrides ) {
646646 console . log ( renderOverrideFindings ( overrideFindings ) ) ;
647647 }
648+
649+ if ( options . usage ) {
650+ if ( scanState . pd001 && scanState . pd001 . length > 0 ) {
651+ console . log ( chalk . red ( "\n[PD001] Override-only Phantom Dependencies Detected (Build Breakers)" ) ) ;
652+ scanState . pd001 . forEach ( pkg => console . log ( ` - ${ pkg } ` ) ) ;
653+ }
654+ if ( scanState . pd002 && scanState . pd002 . length > 0 ) {
655+ console . log ( chalk . yellow ( "\n[PD002] Transitive-only Phantom Dependencies Detected" ) ) ;
656+ scanState . pd002 . forEach ( pkg => console . log ( ` - ${ pkg } ` ) ) ;
657+ }
658+ if ( ( scanState . pd001 && scanState . pd001 . length > 0 ) || ( scanState . pd002 && scanState . pd002 . length > 0 ) ) {
659+ console . log ( chalk . gray ( "\nAction: Declare these dependencies explicitly in your package.json.\n" ) ) ;
660+ }
661+ }
648662 }
649663 }
650664
@@ -697,7 +711,9 @@ if (parsedArgs) {
697711 const reachesFailOn = ( f : { severity : SeverityLabel } ) =>
698712 severityOrder [ f . severity ] >= severityOrder [ failLevel ] ;
699713 const shouldFail =
700- scanState . sorted . some ( reachesFailOn ) || overrideFindings . some ( reachesFailOn ) ;
714+ scanState . sorted . some ( reachesFailOn ) ||
715+ overrideFindings . some ( reachesFailOn ) ||
716+ ( options . usage && ( ( scanState . pd001 && scanState . pd001 . length > 0 ) || ( scanState . pd002 && scanState . pd002 . length > 0 ) ) ) ;
701717 const exitCode = shouldFail ? 1 : 0 ;
702718
703719 // Emit scan.finished event and close audit-log
@@ -773,11 +789,32 @@ async function scanProject(params: {
773789 scanFilePath : params . scanInput . filePath ,
774790 } , params . debugLog ) ;
775791
792+ let pd001 : string [ ] = [ ] ;
793+ let pd002 : string [ ] = [ ] ;
794+
776795 if ( params . options . usage ) {
777- logInfo ( `Scanning project source for usage hints...` , params . options ) ;
796+ logInfo ( `Scanning project source for usage hints and phantom dependencies ...` , params . options ) ;
778797 const usageStartedAt = Date . now ( ) ;
779- const pkgNames = new Set ( findings . map ( f => f . pkg . name ) ) ;
780- const usageData = scanProjectForPackageUsage ( params . projectPath , pkgNames ) ;
798+
799+ // Pass undefined to get ALL imported packages for phantom dependency detection
800+ const usageData = scanProjectForPackageUsage ( params . projectPath ) ;
801+
802+ const allLockfilePackages = new Set ( params . scanInput . packages . map ( p => p . name ) ) ;
803+ const overridesAndResolutions = readOverridesAndResolutions ( params . projectPath ) ;
804+ const directDeps = directDependencyNames || new Set < string > ( ) ;
805+
806+ for ( const importedPkg of Object . keys ( usageData ) ) {
807+ if ( usageData [ importedPkg ] . length === 0 ) continue ;
808+
809+ if ( ! directDeps . has ( importedPkg ) ) {
810+ if ( overridesAndResolutions . has ( importedPkg ) ) {
811+ pd001 . push ( importedPkg ) ;
812+ } else if ( allLockfilePackages . has ( importedPkg ) ) {
813+ pd002 . push ( importedPkg ) ;
814+ }
815+ }
816+ }
817+
781818 let matchedPackages = 0 ;
782819 for ( const finding of findings ) {
783820 const files = usageData [ finding . pkg . name ] ;
@@ -794,8 +831,10 @@ async function scanProject(params: {
794831 if ( params . options . debug ) {
795832 params . debugLog ( "Usage scan" , {
796833 durationMs : Date . now ( ) - usageStartedAt ,
797- packagesChecked : pkgNames . size ,
834+ packagesChecked : Object . keys ( usageData ) . length ,
798835 matchedPackages,
836+ pd001 : pd001 . length ,
837+ pd002 : pd002 . length ,
799838 } ) ;
800839 }
801840 }
@@ -828,6 +867,8 @@ async function scanProject(params: {
828867 tableFindings,
829868 suggestedFixCommands,
830869 allPackages : params . scanInput . packages ,
870+ pd001,
871+ pd002,
831872 } ;
832873}
833874
0 commit comments