Skip to content

Commit 5533347

Browse files
committed
Selective versions resolutions
1 parent 572187b commit 5533347

File tree

1 file changed

+278
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)