@@ -11,78 +11,95 @@ export class PnpmNodeModulesCollector extends NodeModulesCollector<PnpmDependenc
1111 }
1212
1313 protected getArgs ( ) : string [ ] {
14- return [ "list" , "--prod" , "--json" , "--depth" , "Infinity" ]
14+ return [ "list" , "--prod" , "--json" , "--depth" , "Infinity" , "--long" ]
1515 }
1616
17- protected async extractProductionDependencyGraph ( tree : PnpmDependency , dependencyId : string ) {
18- if ( this . productionGraph [ dependencyId ] ) {
19- return
20- }
21-
22- const getProductionDependencies = async ( depTree : PnpmDependency ) : Promise < { prodDeps : Record < string , string > ; optionalDependencies : Record < string , string > } | null > => {
17+ private async resolveActualPath ( depTree : PnpmDependency ) : Promise < string > {
18+ // If using hoisted mode, try to find the package at the hoisted location first
19+ if ( await this . isHoisted . value ) {
2320 const packageName = depTree . name || depTree . from
24- if ( isEmptyOrSpaces ( packageName ) ) {
25- log . error ( depTree , `Cannot determine production dependencies for package with empty name` )
26- throw new Error ( `Cannot compute production dependencies for package with empty name: ${ packageName } ` )
21+ if ( packageName ) {
22+ const hoistedPath = path . join ( this . rootDir , "node_modules" , packageName )
23+ if ( await this . existsMemoized ( hoistedPath ) ) {
24+ return hoistedPath
25+ }
2726 }
27+ }
28+ // Fall back to the reported path (which might be the .pnpm store path)
29+ return depTree . path
30+ }
2831
29- const p = path . normalize ( ( await this . resolvePackageDir ( packageName , depTree . path ) ) ?? ( await this . resolvePath ( depTree . path ) ) )
30- const pkgJsonPath = path . join ( p , "package.json" )
32+ private async getProductionDependencies ( depTree : PnpmDependency ) : Promise < { path : string ; prodDeps : Record < string , string > ; optionalDependencies : Record < string , string > } > {
33+ const packageName = depTree . name || depTree . from
34+ if ( isEmptyOrSpaces ( packageName ) ) {
35+ log . error ( depTree , `Cannot determine production dependencies for package with empty name` )
36+ throw new Error ( `Cannot compute production dependencies for package with empty name: ${ packageName } ` )
37+ }
3138
32- let packageJson : PackageJson
33- try {
34- packageJson = this . requireMemoized ( pkgJsonPath )
35- } catch ( error : any ) {
36- log . warn ( null , `Failed to read package.json for ${ p } : ${ error . message } ` )
37- return null
38- }
39- return { prodDeps : { ...packageJson . dependencies , ...packageJson . optionalDependencies } , optionalDependencies : { ...packageJson . optionalDependencies } }
39+ const actualPath = await this . resolveActualPath ( depTree )
40+ const resolvedLocalPath = await this . resolvePath ( actualPath )
41+ const p = path . normalize ( resolvedLocalPath )
42+ const pkgJsonPath = path . join ( p , "package.json" )
43+
44+ let packageJson : PackageJson
45+ try {
46+ packageJson = this . requireMemoized ( pkgJsonPath )
47+ } catch ( error : any ) {
48+ log . warn ( null , `Failed to read package.json for ${ p } : ${ error . message } ` )
49+ return { path : p , prodDeps : { } , optionalDependencies : { } }
50+ }
51+ return { path : p , prodDeps : { ...packageJson . dependencies , ...packageJson . optionalDependencies } , optionalDependencies : { ...packageJson . optionalDependencies } }
52+ }
53+
54+ protected async extractProductionDependencyGraph ( tree : PnpmDependency , dependencyId : string ) {
55+ if ( this . productionGraph [ dependencyId ] ) {
56+ return
4057 }
4158
4259 const packageName = tree . name || tree . from
43- const json = packageName === dependencyId ? null : await getProductionDependencies ( tree )
60+ const json = packageName === dependencyId ? null : await this . getProductionDependencies ( tree )
4461 const prodDependencies = json ?. prodDeps ?? { ...( tree . dependencies || { } ) , ...( tree . optionalDependencies || { } ) }
4562 if ( prodDependencies == null ) {
4663 this . productionGraph [ dependencyId ] = { dependencies : [ ] }
4764 return
4865 }
4966 const deps = { ...( tree . dependencies || { } ) , ...( tree . optionalDependencies || { } ) }
5067 this . productionGraph [ dependencyId ] = { dependencies : [ ] }
51- const depPromises = Object . entries ( deps )
52- . filter ( ( [ packageName , dependency ] ) => {
53- // First check if it's in production dependencies
54- if ( ! prodDependencies [ packageName ] ) {
55- return false
56- }
68+ const depPromises = Object . entries ( deps ) . map ( async ( [ packageName , dependency ] ) => {
69+ // First check if it's in production dependencies
70+ if ( ! prodDependencies [ packageName ] ) {
71+ return undefined
72+ }
5773
58- // Then check if optional dependency path exists (using memoized version)
59- if ( json ?. optionalDependencies ?. [ packageName ] && ! this . existsSyncMemoized ( dependency . path ) ) {
60- log . debug ( null , `Optional dependency ${ packageName } @${ dependency . version } path doesn't exist: ${ dependency . path } ` )
61- return false
74+ // Then check if optional dependency path exists (using actual resolved path)
75+ if ( json ?. optionalDependencies ?. [ packageName ] ) {
76+ const actualPath = await this . resolveActualPath ( dependency )
77+ if ( ! ( await this . existsMemoized ( actualPath ) ) ) {
78+ log . debug ( null , `Optional dependency ${ packageName } @${ dependency . version } path doesn't exist: ${ actualPath } ` )
79+ return undefined
6280 }
81+ }
82+ const childDependencyId = this . packageVersionString ( dependency )
83+ await this . extractProductionDependencyGraph ( dependency , childDependencyId )
84+ return childDependencyId
85+ } )
6386
64- return true
65- } )
66- . map ( async ( [ , dependency ] ) => {
67- const childDependencyId = this . packageVersionString ( dependency )
68- await this . extractProductionDependencyGraph ( dependency , childDependencyId )
69- return childDependencyId
70- } )
71-
72- const dependencies = await Promise . all ( depPromises )
87+ const dependencies = ( await Promise . all ( depPromises ) ) . filter ( ( id ) : id is string => id !== undefined )
7388 this . productionGraph [ dependencyId ] = { dependencies }
7489 }
7590
7691 protected async collectAllDependencies ( tree : PnpmDependency ) {
7792 // Collect regular dependencies
7893 for ( const [ key , value ] of Object . entries ( tree . dependencies || { } ) ) {
79- this . allDependencies . set ( `${ key } @${ value . version } ` , value )
94+ const json = await this . getProductionDependencies ( value )
95+ this . allDependencies . set ( `${ key } @${ value . version } ` , { ...value , path : json . path } )
8096 await this . collectAllDependencies ( value )
8197 }
8298
8399 // Collect optional dependencies if they exist
84100 for ( const [ key , value ] of Object . entries ( tree . optionalDependencies || { } ) ) {
85- this . allDependencies . set ( `${ key } @${ value . version } ` , value )
101+ const json = await this . getProductionDependencies ( value )
102+ this . allDependencies . set ( `${ key } @${ value . version } ` , { ...value , path : json . path } )
86103 await this . collectAllDependencies ( value )
87104 }
88105 }
0 commit comments