@@ -133,14 +133,20 @@ func (p *Parser) parseV9(lockFile LockFile) ([]ftypes.Package, []ftypes.Dependen
133133 // The "importers" section contains the dependencies defined in package.json files.
134134 // We need to identify which packages are direct dependencies (vs transitive)
135135 // and which are development dependencies (vs production dependencies).
136- devDeps := make (map [string ]string ) // name -> version for dev dependencies
137- deps := make (map [string ]string ) // name -> version for production dependencies
136+ //
137+ // Sets hold packageID(name, version) so that every (name, version) pair from
138+ // every importer is recorded independently. Using name alone would cause the
139+ // last importer to silently overwrite earlier ones, producing non-deterministic
140+ // dev/prod classification when multiple workspaces pin different versions of the
141+ // same package.
142+ directDevDeps := set .New [string ]() // packageIDs of direct dev dependencies
143+ directProdDeps := set .New [string ]() // packageIDs of direct production dependencies
138144 for _ , importer := range lockFile .Importers {
139145 for n , v := range importer .DevDependencies {
140- devDeps [ n ] = v .Version
146+ directDevDeps . Append ( packageID ( n , p . trimPeerDeps ( v .Version , lockVer )))
141147 }
142148 for n , v := range importer .Dependencies {
143- deps [ n ] = v .Version
149+ directProdDeps . Append ( packageID ( n , p . trimPeerDeps ( v .Version , lockVer )))
144150 }
145151 }
146152
@@ -156,7 +162,8 @@ func (p *Parser) parseV9(lockFile LockFile) ([]ftypes.Package, []ftypes.Dependen
156162 // Try to get the exact version from the "packages" section if available.
157163 // The "packages" section may contain more accurate version information
158164 // for packages installed from non-standard sources (git, local files, etc.).
159- pkgKey := PackageKey (packageID (name , version ))
165+ pkgID := packageID (name , version )
166+ pkgKey := PackageKey (pkgID )
160167 if pkgInfo , ok := lockFile .Packages [pkgKey ]; ok && pkgInfo .Version != "" {
161168 parsedVer = pkgInfo .Version
162169 }
@@ -167,12 +174,11 @@ func (p *Parser) parseV9(lockFile LockFile) ([]ftypes.Package, []ftypes.Dependen
167174 dev := true
168175 relationship := ftypes .RelationshipIndirect // Assume transitive by default
169176
170- // Check if this package matches a direct dev dependency
171- if v , ok := devDeps [ name ]; ok && p . trimPeerDeps ( v , lockVer ) == version {
177+ // Check if this package matches a direct dev or production dependency.
178+ if directDevDeps . Contains ( pkgID ) {
172179 relationship = ftypes .RelationshipDirect
173180 }
174- // Check if this package matches a direct production dependency
175- if v , ok := deps [name ]; ok && p .trimPeerDeps (v , lockVer ) == version {
181+ if directProdDeps .Contains (pkgID ) {
176182 relationship = ftypes .RelationshipDirect
177183 dev = false // This is a production dependency, not a dev dependency
178184 }
0 commit comments