Skip to content

fix(resolvePeers): fix deduplication when version missmatch#6606

Merged
zkochan merged 4 commits into
pnpm:mainfrom
klingenm:dedupe-peer-dependents-fix
Jun 2, 2023
Merged

fix(resolvePeers): fix deduplication when version missmatch#6606
zkochan merged 4 commits into
pnpm:mainfrom
klingenm:dedupe-peer-dependents-fix

Conversation

@klingenm

Copy link
Copy Markdown

resolves: #6605

When dedupe-peer-dependents is enabled (default), use the path (not id) to
determine compatibility.

When multiple dependency groups can be deduplicated, the
latter ones are sorted according to number of peers to allow them to
benefit from deduplication.

@klingenm klingenm requested a review from zkochan as a code owner May 29, 2023 11:18
@welcome

welcome Bot commented May 29, 2023

Copy link
Copy Markdown

💖 Thanks for opening this pull request! 💖
Please be patient and we will get back to you as soon as we can.

@klingenm

This comment was marked as outdated.

@zkochan

zkochan commented May 30, 2023

Copy link
Copy Markdown
Member

You need to run pnpm test, not pnpm test:e2e. To run a single test file, put the path to the test file after jest in the scripts. To run a single test in that test file, use .only in the code.

@klingenm

This comment was marked as resolved.

@klingenm

Copy link
Copy Markdown
Author

I don't understand why. But having jest installed globally messed it up. Uninstalling it fixed it.

@klingenm klingenm force-pushed the dedupe-peer-dependents-fix branch from 3239e9e to 92f805e Compare May 31, 2023 11:16
@klingenm

klingenm commented May 31, 2023

Copy link
Copy Markdown
Author

The failing tests was due to "nested" peer dependencies. The test deduplicate packages that have peers (https://github.com/klingenm/pnpm/blob/dedupe-peer-dependents-fix/pkg-manager/core/test/install/peerDependencies.ts#L1309) caught the case where one peer dependency affects the resolved path of another peer.

I solved it by keeping track of unresolved duplicates and taking several deduplication passes until nothing is deduplicated.

There is still one failing test locally;

 FAIL  test/breakingChanges.ts
  ● fail on non-compatible pnpm-lock.yaml when frozen lockfile installation is used

    expect(received).toBe(expected) // Object.is equality

    Expected: "ERR_PNPM_LOCKFILE_BREAKING_CHANGE"
    Received: "ERR_PNPM_BROKEN_LOCKFILE"

      120 |   } catch (err: any) { // eslint-disable-line
      121 |     if (err.message === 'should have failed') throw err
    > 122 |     expect(err.code).toBe('ERR_PNPM_LOCKFILE_BREAKING_CHANGE')
          |                      ^
      123 |   }
      124 | })
      125 |

      at Object.<anonymous> (test/breakingChanges.ts:122:22)

Is this expected?

@zkochan

zkochan commented May 31, 2023

Copy link
Copy Markdown
Member

yes, I think that one breaks for me too.

Comment on lines +123 to +128
Object.values(depGraph).forEach((node) => {
node.children = mapValues((childDepPath) => depPathsMap[childDepPath] ?? childDepPath, node.children)
})
for (const { id } of opts.projects) {
dependenciesByProjectId[id] = mapValues((depPath) => depPathsMap[depPath] ?? depPath, dependenciesByProjectId[id])
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to do this on each cycle? Can't we do it one time after the while cycle?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're correct. Moved it out.

I was also getting paranoid about the while loop. Added a maxPasses guard. In the worst case scenario, where every duplicated package is dependent on another duplicated package, the max number of passes that can do anything is same as duplicates.length.

When dedupe-peer-dependents is enabled (default), use the path to
determine compatibility.

When multiple dependency groups can be deduplicated, the
latter ones are sorted according to number of peers to allow them to
benefit from deduplication.
@klingenm klingenm force-pushed the dedupe-peer-dependents-fix branch from 92f805e to a834d26 Compare June 1, 2023 07:02
Comment on lines +125 to +127
Object.values(depGraph).forEach((node) => {
node.children = mapValues((childDepPath) => depPathsMap[childDepPath] ?? childDepPath, node.children)
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about this one? Can this be moved out from the loop?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that is required for pkg-manager/resolve-dependencies/src/resolvePeers.ts:196-197 to get the dependency path for any peers that have been deduplicated in a previous pass.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty expensive operation. Can at least Object.values(depGraph) be moved to a variable?

@klingenm

klingenm commented Jun 2, 2023

Copy link
Copy Markdown
Author

One could create a map for the affected nodes per duplicated dependency. That way one can replace one the affected keys in the affected nodes.

For that I would need a canonical way of translating the pkgId (/abc/1.0.0) to the pkg key in node.children. That way there would only be one pass over the nodes.

I can do it on Monday.

@zkochan

zkochan commented Jun 2, 2023

Copy link
Copy Markdown
Member

Yes, that solution actually sounds better to me.

@zkochan

zkochan commented Jun 2, 2023

Copy link
Copy Markdown
Member

I'll merge it for now. You can make your optimisations in a separate PR.

@zkochan zkochan merged commit e83eacd into pnpm:main Jun 2, 2023
@welcome

welcome Bot commented Jun 2, 2023

Copy link
Copy Markdown

Congrats on merging your first pull request! 🎉🎉🎉

zkochan added a commit that referenced this pull request Jun 2, 2023
When dedupe-peer-dependents is enabled (default), use the path to
determine compatibility.

When multiple dependency groups can be deduplicated, the
latter ones are sorted according to number of peers to allow them to
benefit from deduplication.

close #6605

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
@klingenm

klingenm commented Jun 8, 2023

Copy link
Copy Markdown
Author

I'll merge it for now. You can make your optimisations in a separate PR.

I got sidetracked. I'll have a look at the optimization, but not sure when it will happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dedupe-peer-dependents disregards version information

3 participants