Reading yarn.lock

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, package-lock.json.

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:

clipboard@2.0.4, 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:

clipboard@2.0.4, 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 (clipboard@2.0.4) 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.

  version "2.0.4"

This means that all the dependencies specified in the header (clipboard@2.0.4, clipboard@^2.0.0, clipboard@^2.0.1) will be resolved with this specific version of the package, clipboard@2.0.4 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 clipboard@2.0.4. 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 clipboard@2.0.4, 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 dependencies, devDependencies, 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 yarn.lock.

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. Manually inspecting it can give us a lot of information about our tree. I have used yarn.lock for:

  • Find out who is bringing some dependency. For example, who is importing acorn@6.1.1? Open yarn.lock, find the relevant block (tip: it will have acorn in the header, and version: "6.1.1"), and then search for each version range specified in the header (remember to change the syntax, i.e. add the @)

  • 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.