How should we resolve conflicting addons and dependencies?


#1

Sometimes different conflicting versions of an addon can appear in the dep tree of an ember app, which causes unexpected behavior. Thanks for ember-cli-dependency-lint, since it helps detect when there are multiple versions of a dependency in the tree, but does not do much for us when trying to figure out how best to handle conflicting versions.

Yarn gives us the ability to manually resolve these addons (or packages) (even for nested dependencies) via Selective Dependency Resolution which seems to have worked.

But is this the recommended Ember way to resolve conflicting versions of transitive deps? or should we avoid doing it this way? Thanks for any info that anyone can provide!


#2

I think you’re doing the best that can be done. When the required version are in actual conflict, there’s no easy answer.

For example, if you have addon-one which depends on ember-concurrency: "^0.7.0" and addon-two which depends on ember-concurrency: "^0.8.0", the strict answer is that there is no valid solution. But in practice, often those ranges are more conservative than necessary, and maybe addon-one works fine with ember-concurrency 0.8.0. The only way to be sure is to test it. If it does work, you’re safe using selective dependency resolution, and it’s nice to also make a PR to addon-one expanding its semver range so that other people don’t have the same problem.


#3

Agreed! I think (in general) this is a pretty difficult problem to solve generically (and is applicable to many other tech stacks than just ours).

There are some things that we should do to make things a bit better out of the box:

  • making ember-cli-dependency-lint a “built-in” (likely merging in some way with the similarly named ember-dependency-checker)
  • providing better merging behaviors in ember-cli when conflicting versions do exist (e.g. erroring during build, “scoping” to the major, etc)

#4

I think the unpredictability is the hardest part. If we could at least say “The version declared in the app’s package json will always tack precedence over any addon’s version” then it would be slightly more clear.


#5

Agreed. Today, this would be quite hard to implement within the normal app build pipeline, but I believe the “package work” will enable this (at least conceptually) to work a bit better…


#6

Yeah, I totally think this is a great approach. I believe this is how npm works when developing/building npm packages (with nested conflicting transitive deps). So it may be advantageous for Ember’s build steps to follow that.

@rwjblue The version of a conflicting transitive dependency Ember chooses for its build pipeline is still a mystery to me though. I haven’t looked too deeply into the code but, just out of curiosity, how does Ember decide which version a dependency should resolve to? Or does it just try to clobber all versions together? Which version takes precedence? This is all assuming yarn isn’t used and you don’t get the benefit of single resolutions in a yarn.lock file.


#7

Sadly, this is what is done today. :sob:


#8

I think I read somewhere that using yarn installs only one version of a dep by default (even if it appears multiple times in the dep tree). So, instead of clobbering, can the build just use that (or fallback to the root level dep)?


#9

Yarn tries, but it can’t make promises. It’s still possible for two dependencies to require non-overlapping versions of a third dependency, in which case yarn does the normal NPM thing and gives you two nested copies.