-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow peer dependencies with stricter version ranges #433
Comments
Can you clarify whether you think the two parts of your proposal ("Allow optional peer dependencies" and "Allow constraining @types/* dependencies to something stricter than *") are both necessary or if only one or the other is necessary? I could be wrong, but I thought the latter can be done today by adding a package.json and specifying the version range. (This currently requires adding to |
Optional peer dependencies are the more pressing issue. If they keep the Getting rid of the So in my mind these things can be implemented independently. But optional peer dependencies is "more necessary" than strict version ranges if that makes sense. I just grouped them in a single issue since the concrete examples are affected by both.
I was under the impression that this didn't work. But it's been a few years since I tried and maybe it's now possible. |
I think it would be helpful to narrow this issue to just be about allowing optional peer dependencies and move the stricter version ranges to a separate issue. Getting rid of the |
|
Some thinking here is clearly off - if your types depend on Moreover, all of DT is tested to be internally consistent, so the latest versions of all packages on DT should always work together - enumerating how far back in time, and for what version ranges, that holds is practically impossible over a shifting set of dependencies, hence the What's odd, to me, is that while a package manage is willing to update the outermost package in its tree, why it would be unwilling to update the inner one even though the version specifier allows it? Especially if it already deduped the versions to a single copy? (The package manager seems like it would have to do more work to move the old install to a new location!) Minimally, I know if you blow away your lockfile and install again, they'll unify to the single valid top-level install again, which is decidedly not what would be the case of version ranges were stricter - you'd just be guaranteed to have the version conflict. |
TL;DR: We'll never allow optional peer dependencies - those don't make any sense for types. Required (and autoinstalled) peer dependencies make more sense, but don't seem to carry more meaning than normal deps do for our usecases - the peer relationship at install time is not strictly required (all it does is elevate when conflicts occur to install time). Stricter version ranges (not |
The root issue is that package managers will try to install the latest version allowed within a range. So if you have an app that uses I agree making it an optional peer dependency is probably a red herring and probably got carried over the by the discussion of how non- |
Basically, it's easier to change it to a peerDependency then reconsider how all package managers currently work. |
A peer dependency doesn't "solve" the problem though - if you replace |
I'm just talking about the peer dependency part of this proposal, I think the discussion regarding |
I think it should be a peer dependency on |
Yeah for
At the time of the publishing,yes. But not for eternity which
Because that would change the code of the an outermost dependency even though it wasn't updated. If A depends on B, my app depends on A and B and I want to update B then the B that A uses should not change if I have a lockfile. That's one reason why lockfiles exist. But that's not an arugment we have to have. That's simply the reality of virtually every JS package managers. It's not constructive to ignore that reality.
That's inaccurate. I'm not asking for exact version ranges. I'm asking for stricter version ranges than I'm not sure how I can better describe the problem. There's a current, concrete example why
It solves the problem by allowing packages to resolve the conflict. They need to publish updates if they support later versions. The problem that's solved is at the app level where we currently have no workaround aside from lockfile surgeries. |
Every package depending on Otherwise you can create dependency graphs on install (e.g. fresh install of a boilerplate without lockfile) where incompatible types are installed because someone put |
Semver is a lie for types - semver compatible runtime changes in the underlying package can create breaking types. Semver incompatible versions of the underlying package (due to behavioral differences) can easily have the same or compatible types. Hence why the only stable state for types is latest everything, because that's the only thing we've tested to work. |
If I add If you say that SemVer is a lie for types, why do DT packages sometimes declare stricter versions than @weswigham Do your statements reflect the opinion of the TypeScript team? |
Because |
It still feels like this should be two separate issues so we can narrow our discussion. The problem/solution of dependencies vs. peerDependencies is distinct from the problem/solution of how strict the version ranges should be. They just both happened to be problems that arose from the breaking changes in |
Absolutely, the peer dep thing and the not- |
On the peer dep thing, we have an age old issue on the (archived old version of the) |
DefinitelyTyped is a clever service for the TypeScript Ecosystem. The real "correct" fix is for types to be shipped by the official runtime-packages instead of being provided by DefinitelyTyped. Then they would be versioned exactly like the packages they belong to. In practice, many DefinitelyTyped definitions are maintained by external communities, and aren't necessarily shipped simultaneously with their source-packages, there are always going to be warts and weak points. Starting with There's no point throwing the baby out with the bathwater. Let's refocus this ticket on the discussion of if "peerDependencies with stricter version ranges" is a valid enhancement to DefinitelyTyped capabilities that is worth the cost, and if there are obvious traps that we will fall into. An additional point to discuss is the impact if "all packages" follow this recommendation. Do we lead to excess thrash on DT's repo? (Because people have to make new versions to increase the upper bound every time the source package updates?) Other problems? |
I mean, we technically make exceptions to the |
Looks like Yarn is experimenting with trying to install the minimal number of versions that satisfies all ranges (source) which could help this situation. |
that won't help if you have dependencies specifically on multiple versions of react in your monorepo. |
The more common case is where only a single version of React is being used, but multiple versions of |
Hey folks, bumping this thread because it seems to have stalled. I would also like to raise a variant of the problem that I'm having in my company's monorepo: we're trying to support having different (major) versions of react for internal applications, but sharing our testing framework and build tools - so we all use the same webpack, typescript, jest, storybook etc. So we're trying to use yarn workspaces for this. And this brings me to:
Resolutions has been a usable workaround for non-workspace usage of yarn - every package.json got it's own lockfile and therefore could specify it's own resolutions for @types/react to force deduplication. However, we're trying to adopt workspaces so that we can use yarn constraints and more easily share our build and test tooling, and this has put us in a bind - all of our packages in the same workspace is clearly optimal for the type of sharing we want to do for build & dev tools, but leaves us doing very ad-hoc resolution overrides of @types/react as all our different internal applications are on various react 16, 17 and 18 variants, and yarn resolutions allow us only to override resolution of specific semver specifiers globally - not on a package by package basis within a workspace. So a dependency on What's the main objection to allowing @types/react as a peerDependency? testability of the types? |
After the big pnpm monorepo restructuring, I don't believe there's a technical blocker for DT packages to list peer deps; every package has a At this point, I personally think it would be safe to allow them on packages where they're clearly intended to be installed with another package, e.g. plugin packages or react (where the user is expected to install react anyway). I had meant to comment here after that refactor (this has been brought up on the DT channel on the TS discord previously), but hadn't had time. (I am saying this conditionally, since I think it would be a bad idea to use peer deps for everything given there are still package managers which do not auto install them.) |
sweet. so what's the road forward here? I would like see the ecosystem get to a place where all the packages that specify react as a peer dep have their corresponding @types/ package use @types/react as a peer dep. I imagine there will similarly be use for webpack, rollup, etc. ecosystems to do the same |
Realistically, just a code change to this repo to process peer deps, put them through the same checks as other deps, etc, which will be similar to code which checks devDeps (also new). |
I've put up #1072, though I don't quite know how we can enforce the general rule of "don't use peer deps unless it's going to be the case that a user will install that peer dep to make their package work". E.g., |
awesome. any chance we could mass convert @types/react dependencies too? would that be better tracked as a follow up issue? |
"mass conversion" is the thing that worries me most, accidentally unloading a load of yarn errors onto people who didn't have them previously... but react is the most obvious use case for this. I don't know the right place to track this besides a DT discussion or something; bulk changing these isn't really in scope of this repo's code. |
One thing I'm now wondering is whether or not the publisher should publish stub packages using peer deps too. That's also a case where we would expect a user to install the real package. |
Continuation of microsoft/types-publisher#431.
Current state
When you import from a package hosted in the DefinitelyTyped repository in a package that's also hosted in DefinitelyTyped, the types publisher will automatically declare a direct
dependency
on that types package matching any version. For example, if you have a packagereact-library
that imports fromreact
then thepackage.json
of@types/react-library
will look something like this:Problems
Combination of the described problems are encountered in
duplication of
@types/*
packagesGiven an existing module graph similar to
If you want to upgrade to React 18 you would need to update
@types/react
withnpm install @types/react@18
.However,
npm
(andyarn
and probably other package managers) make sure that this install does not alter the dependency tree too much (if you're using lockfiles). What you end up with is something likeNow your dependency tree contains multiple version of a types package. In this specific case these packages are incompatible.
To fix, one would have to apply one of the workarounds described in facebook/react#24304 (comment) which isn't trivial at all.
After all, package managers are supposed to resolve these issues. But they can't if we provide them with inaccurate information.
*
is not forwards compatibleWhile currently published packages might work perfectly fine with all published versions of
@types/react
at the time, there's no guarantee that continues to work. It essentially requires that no breaking changes are made at all which defeats the purpose of SemVer major releases.Proposal
Allow peer dependencies
React is generally a peer dependency and therefore it types should as well.
If we could move
@types/react
fromdependencies
to peer dependencies withwe would ensure types deduplication since the package owner would defines the version of
@types/react
a single time (viadependencies
ordevDependency
.Allow constraining
@types/*
dependencies to something stricter than*
Either let maintainers decide the range manually or infer the range from the versions it's currently tested on. For example,
@types/react-library
runs tests with@types/react
v15, v16, v17 and latest. In that case we check what version the current release has (or will be) and declare that.This way an update to
@types/react-library
will also bump versions ofdependencies
and therefore propagate bug fixes and features of those dependencies upon release.related issues
The text was updated successfully, but these errors were encountered: