Skip to content

Commit 98693bd

Browse files
committed
Selective versions resolutions
1 parent 572187b commit 98693bd

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
- Start Date: 2017-05-21
2+
- RFC PR: (leave this empty)
3+
- Yarn Issue: (leave this empty)
4+
5+
# Summary
6+
7+
Allow to select a nested dependency version via the `resolutions` field of
8+
the `package.json` file.
9+
10+
# Motivation
11+
12+
The motivation was initially discussed in
13+
[yarnpkg/yarn#2763](https://github.com/yarnpkg/yarn/issues/2763).
14+
15+
Basically, the problem with the current behaviour of yarn is that it is
16+
not possible to force the use of a particular version for a nested dependency.
17+
18+
## Example
19+
20+
For example, given the following content in the `package.json`:
21+
```json
22+
"devDependencies": {
23+
"@angular/cli": "1.0.3",
24+
"typescript": "2.3.2"
25+
}
26+
```
27+
28+
The `yarn.lock` file will contain:
29+
```
30+
"typescript@>=2.0.0 <2.3.0":
31+
version "2.2.2"
32+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.2.tgz#606022508479b55ffa368b58fee963a03dfd7b0c"
33+
34+
35+
version "2.3.2"
36+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984"
37+
```
38+
39+
Also, there will be:
40+
- `[email protected]` in `node_modules/typescript`
41+
- `[email protected]` in `node_modules/@angular/cli/node_modules`.
42+
43+
## Problem
44+
45+
In this context, it is impossible to force the use of `[email protected]` for
46+
the whole project (except by flattening the whole project, which we don't want).
47+
48+
It makes sense for typescript as the user intent is clearly to use typescript
49+
2.3.2 for compiling all its project, and with the current behaviour, the angular
50+
CLI (responsible of compiling `.ts` files) will simply use the 2.2.2 version
51+
from its `node_modules`.
52+
53+
## Variations of the original problem
54+
55+
Similarly, even using such a content for `package.json`:
56+
```json
57+
"devDependencies": {
58+
"@angular/cli": "1.0.3"
59+
}
60+
```
61+
62+
The need could arise for forcing the use of `[email protected]` (or
63+
`[email protected]` for that matter).
64+
65+
In these example, the need does not seem very important (the user could maybe
66+
use `[email protected]` or ask the `@angular/cli` dev team to relax its
67+
constraints on typescript), but there could be cases where a nested dependency introduces a bug and the project developper would want to set a specific
68+
version for it (see for example this
69+
[comment](https://github.com/yarnpkg/yarn/issues/2763#issuecomment-302682844)).
70+
71+
## Related scenario (out of scope of this document)
72+
73+
An extension of this motivation is also the potential need for mapping nested dependencies to others. For example a project developper could want to map `typescript@>=2.0.0 <2.3.0` to `[email protected]`.
74+
75+
See alternatives solutions below also.
76+
77+
# Detailed design
78+
79+
The proposed solution is to make the `resolutions` field of the `package.json`
80+
file to be considered all the time and on a per-package basis.
81+
82+
When a nested dependency is being resolved by yarn, if the `resolutions` field
83+
contains a version for this package, then this version is used instead.
84+
85+
All the examples are given with exact dependencies, but note that putting a
86+
non-exact specification in the `resolutions` field should be accepted and
87+
resolved by yarn like it usually does.
88+
89+
## Example
90+
91+
For example with:
92+
```json
93+
"devDependencies": {
94+
"@angular/cli": "1.0.3"
95+
},
96+
"resolutions": {
97+
"typescript": "2.3.2"
98+
}
99+
```
100+
101+
yarn will use `[email protected]` for every nested dependency to `typescript`
102+
and will behave as expected with respect to the `node_modules` folder by not
103+
duplicating typescript installation.
104+
105+
## Relation to non-nest dependencies
106+
107+
The `devDependencies` and `dependencies` fields always take precedence over the
108+
`resolutions` field: if the user defines explicitely a dependency there,
109+
it means that he wants that version, even if it's specified with a non-exact
110+
specification. So the `resolutions` field only applies to nested-dependencies.
111+
112+
## Relation to the `--flat` option
113+
114+
The `--flat` option becomes thus a way to populate the resolutions field for
115+
the whole project, as it already does.
116+
But the `resolutions` field is always considered by yarn, even if `--flat` is
117+
not specified.
118+
119+
Inceidently, this resolves this strange situation when two developers would be
120+
working on the same project, and one is using `--flat` while the other is not,
121+
and they would get different `node_modules` contents because of that.
122+
123+
## `yarn.lock`
124+
125+
This design implies that it is possible to have for a given version
126+
specification (e.g., `>=2.0.0 <2.3.0`) a resolved version that is incompatible
127+
with it (e.g., `2.3.2`).
128+
It is acceptable as long as it is explicitly asked by the user.
129+
130+
It is currently the case that such situation would make yarn unhappy and
131+
provoke the modification of the `yarn.lock` (see
132+
[yarnpkg/yarn#3420](https://github.com/yarnpkg/yarn/issues/3420)).
133+
134+
This feature would remove the need for this behaviour of yarn.
135+
136+
## Warnings in logs
137+
138+
yarn would need to warn about the following situations:
139+
- Unused resolutions
140+
- Incompatible resolutions: see the above section about `yarn.lock`.
141+
Incompatible resolutions should be accepted but warned about since it could
142+
lead to unwanted behaviour.
143+
- ? (see open questions below)
144+
145+
# How We Teach This
146+
147+
This won't have much impact as it extends the current behaviour by adding
148+
functionality.
149+
150+
The only breaking change is that `resolutions` is being considered all the time,
151+
but that won't surprise people, this will make yarn behaviour simply more
152+
consistent than before (see the comment on `--flat` above).
153+
154+
The term "resolution" has the same meaning as before, but it is not under the
155+
sole control of yarn itself anymore, but also under the control of the user
156+
now.
157+
158+
This is an advanced use of yarn, so new users don't really have to know about
159+
it in the beginning.
160+
161+
# Drawbacks
162+
163+
## Teaching
164+
165+
It makes yarn behaviour a bit more complex, even though more useful. So it
166+
can be difficult for users to wrap their head around it. The RFC submitter has
167+
seen it happen many times with maven, which is quite complex but complete in
168+
its dependency management. Users would get confused and it can take time to
169+
understand the implications of manipulation the `resolutions` field (even
170+
though, the chosen solution, compared to the alternatives below, is much
171+
simpler).
172+
173+
## Package management paradigm
174+
175+
Yarn and npm users are highly used to the idea that a dependency can be
176+
present many times in the `node_module`, depending on which package needs it.
177+
This has advantages and inconvenients, but it is one of the specificity of the
178+
npm ecosystem package management.
179+
180+
In this light, taking such as design decision puts yarn a bit farther to such
181+
way of doing thing, and it could be considered a bad direction to go toward.
182+
183+
Some of the alternatives below actually take this into consideration, but are
184+
a bit more complex in terms of expressivity, so were not chosen by the RFC
185+
submitter (see open questions below too).
186+
187+
# Alternatives
188+
189+
There is at least one alternative to the proposed solution, more complex but
190+
more expressive.
191+
192+
## Nested dependencies resolution per dependency
193+
194+
Starting from an example, this solution would take the following form in the
195+
`package.json` file:
196+
```json
197+
"devDependencies": {
198+
"@angular/cli": "1.0.3",
199+
"typescript": "2.3.2"
200+
},
201+
"resolutions": {
202+
"@angular/cli": {
203+
"typescript": "2.0.2"
204+
}
205+
}
206+
```
207+
208+
yarn would use `[email protected]` only for `@angular/cli` (so in
209+
`node_modules/@angular/cli/node_modules`), but keep `[email protected]` in
210+
`node_modules/typescript`.
211+
212+
Basically, this enables the user to specify versions for nested dependencies,
213+
but only in the context of a given dependency.
214+
215+
The fields of the `resolutions` field must only refer to existing entries in
216+
`devDependencies` and `dependencies`.
217+
218+
Of course, if the same version of a nested dependency is used for many
219+
dependencies, yarn will behave as always by keeping it directly in
220+
`node_modules`.
221+
222+
## Mapping version specifications
223+
224+
This is a kind of simplified solution to the "out-of-scope scenario" in the
225+
motivations section above (it maps versions but not dependency names).
226+
227+
It was proposed in this
228+
[comment](https://github.com/yarnpkg/yarn/issues/2763#issuecomment-301896274).
229+
230+
Everything is not totally clear to me, but the idea would be to map a given
231+
version specification to another one.
232+
This would take this form in the `package.json`:
233+
```json
234+
"devDependencies": {
235+
"@angular/cli": "1.0.3",
236+
"typescript": "2.2.2",
237+
"more dependencies..."
238+
},
239+
"mappings": {
240+
"typescript@>=2.0.0 <2.3.0": "[email protected]"
241+
}
242+
```
243+
244+
yarn would then replace matching version specifications with the user's one.
245+
What is problematic with this is that the user has to know that `@angular/cli`
246+
is exactly expressing its dependency to `typescript` as `>=2.0.0 <2.3.0`.
247+
248+
This makes such mappings hard to maintain because they can become ignored if
249+
`@angular/cli` is upgraded and its dependency specification changes, while
250+
the other solutions would only result in
251+
252+
# Unresolved questions
253+
254+
## Is this expressive enough?
255+
256+
As explained in the alternative solutions section, it would be much more
257+
expressive and coherent with the npm ecosystem package management paradigm
258+
to use nested dependency resolutions per project dependency.
259+
Would the loss of simplicity acceptable maybe?
260+
261+
## Warnings in logs
262+
263+
Should yarn warn the user about an incoherence between an explicit dependency
264+
and a resolution. For example if the user specify a dependency to
265+
`[email protected]` and the resolutions field contains `[email protected]`.
266+
For sure if the above alternative solution is chosen, this wouldn't make sense.
267+
268+
Should we warn if a resolutions is incompatible, but still upper-bounded?
269+
For example, forcing version `[email protected]` while a dependency needs version `[email protected]` is
270+
usually less problematic than forcing version `[email protected]` while a dependency needs
271+
272+
The problem with differentiating these situations is that yarn to start giving
273+
lots of semantics to versions and it can give false certainty to the user than
274+
a problematic situation is not problematic. So it may be better to always warn
275+
about incompatible resolutions.

0 commit comments

Comments
 (0)