yarn.lock is the name of the lockfile used by yarn to track dependencies in a project.
Although the file is autogenerated and not meant to be read by a human, the reality is that more often than not you’ll need to understand how the file is structured, to be able to resolve conflicts, provide feedback in Pull Requests, etc. Working with
yarn.lock can be daunting, specially if you have past experience with its NPM counterpart,
However, you’ll find
yarn.lock has a much more simpler structure.
yarn.lock is a collection of “dependency resolution blocks”. Each one of the blocks have a format like:
email@example.com, clipboard@^2.0.0, clipboard@^2.0.1: version "2.0.4" resolved "https://registry..." integrity sha512-Vw26VSLRpJfBofiVaFb.... dependencies: good-listener "^1.2.2" select "^1.1.2" tiny-emitter "^2.0.0"
Let’s go line by line:
firstname.lastname@example.org, clipboard@^2.0.0, clipboard@^2.0.1:
That is the header of the block. Is a list of one or more version specifications separated by a comma. They can be fixed versions (
email@example.com) or ranges (
clipboard@^2.0.1). It means that this block applies to any package in your dependency tree that depends on one of those versions.
This means that all the dependencies specified in the header (
firstname.lastname@example.org, clipboard@^2.0.0, clipboard@^2.0.1) will be resolved with this specific version of the package,
email@example.com in this case.
This has two implications that are worth mentioning:
- No matter where in your dependency tree appears
clipboard@^2.0.1, it will always be resolved with
firstname.lastname@example.org. This is not the case when using NPM.
- No matter how other packages specify a dependency on
clipboard: if it is not one of the 3 versions specified in the header of the block, they will not get
email@example.com, they will get something else.
resolved "https://registry..." integrity sha512-Vw26VSLRpJfBofiVaFb....
These two lines specify where the package will be downloaded from, and a checksum that will be used to validate that the package has not been tampered with.
dependencies: good-listener "^1.2.2" select "^1.1.2" tiny-emitter "^2.0.0"
These are the dependencies of this package. Note that
yarn doesn’t make a difference between
optionalDependencies… That information is already encoded in the
package.json file of the package.
Note that, technically, there is no need to specify the dependencies here.
yarn could extract the same information from
package.json. I believe those dependencies are here to speed up some lookup process, and more importantly, to help humans navigate
The important part is that for each dependency listed here, there will be another block in
yarn.lock specifying how that dependency is resolved. But be careful, the format is slightly different: here it is
good-listener "^1.2.2", but the block defining that package will have
good-listener@^1.2.2 in its title.
As you can see, the content of
yarn.lock is not really complicated. However, inspecting it can give us a lot of information about our tree. I have used
- Find out who is bringing some dependency. For example, who is importing
yarn.lock, find the relevant block (tip: it will have
acornin the header, and
version: "6.1.1", and then search for each version range specified in the header (remember to change the syntax)
- FInd duplicated packages: the best way is to search for part of the URL, for example search for
https://registry.yarnpkg.com/loglevelnext/. For each occurrence of that URL, it means that a different version of the package will be present in your tree.
- Continuing with duplicated packages: once you have find all the blocks that install a dependency, you can check their headers to find the versions ranges. It is very useful to find out why a super old version of a package is still present on your tree.