β’ 6 min read
How to update module subdependencies with yarn
When might you want to do this?
Example
There is security vulnerability in a node module that is included in the proeject you're working on. Let's pretend that the vulnerability impacts braces
versions <=3.0.2. How do you resolve this?
1. Start by running yarn why braces
yarn why
is a handy command that can help us figure out why a dependency exists in in your project so let's start with that.
The output of yarn why braces
in an example project:
yarn why v1.22.4
[1/4] π€ Why do we have the module "braces"...?
[2/4] π Initialising dependency graph...
[3/4] π Finding dependency...
[4/4] π‘ Calculating file sizes...
=> Found "braces@3.0.2"
info Has been hoisted to "braces"
info Reasons this module exists
- Hoisted from "karma#braces"
- Hoisted from "ts-loader#micromatch#braces"
- Hoisted from "karma#chokidar#braces"
- Hoisted from "copy-webpack-plugin#fast-glob#micromatch#braces"
- Hoisted from "@types#browser-sync#@types#chokidar#chokidar#braces"
- Hoisted from "@rails#webpacker#webpack#watchpack#chokidar#braces"
info Disk size without dependencies: "80KB"
info Disk size with unique dependencies: "104KB"
info Disk size with transitive dependencies: "244KB"
info Number of shared dependencies: 4
=> Found "micromatch#braces@2.3.2"
info This module exists because "micromatch" depends on it.
info Disk size without dependencies: "120KB"
info Disk size with unique dependencies: "1.42MB"
info Disk size with transitive dependencies: "4MB"
info Number of shared dependencies: 30
=> Found "chokidar#braces@2.3.2"
info This module exists because "chokidar" depends on it.
info Disk size without dependencies: "76KB"
info Disk size with unique dependencies: "1.38MB"
info Disk size with transitive dependencies: "3.96MB"
info Number of shared dependencies: 30
β¨ Done in 0.80s.
2. π Analyze the output
Look for all the lines that start with "Found". Each of these lines gives us information about a version of braces
that this project depends on. In this case we can see that our project depends on braces
versions 3.0.2
and 2.3.2
. This means that in our project's yarn.lock
file we should expect to see 2 entries starting with braces@
. One will have its version
property set to 3.0.2
and the other, 2.3.2
. Here's a full output of these from yarn.lock
:
braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
integrity sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=
dependencies:
arr-flatten "^1.1.0"
array-unique "^0.3.2"
extend-shallow "^2.0.1"
fill-range "^4.0.0"
isobject "^3.0.1"
repeat-element "^1.1.2"
snapdragon "^0.8.1"
snapdragon-node "^2.0.1"
split-string "^3.0.2"
to-regex "^3.0.1"
braces@^3.0.1, braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha1-NFThpGLujVmeI23zNs2epPiv4Qc=
dependencies:
fill-range "^7.0.1"
In our case, the next lines we care about tell us why braces
exists via output of the dependency tree leaves that end with braces
. The left-most leaves are dependencies we've declared in the project's package.json
file.
Looking at a few of these lines we see that:
karma
has a direct dependency onbraces
.ts-loader
has a dependency onbraces
via its dependency onmicromatch
.karma
also has a dependency onbraces
via its dependency onchokidar
.- etc
3. π Update the version of braces
that our project requires
Lets assume the maintainer of the braces
module has published an updated version (3.0.3
) that resolves the vulnerability*, our goal now is to update our project's dependency tree so that it only resolves its braces
dependencies to braces@3.0.3
.
*If the maintainer hasn't shipped a fix, open an issue on their project repository with details about the vulnerability and the fix.
Here are some options for updating the braces
version that our project depends on. I've ordered these according to how effective the solution is, with minimal side-effects.
1. βοΈπ³ Prune tree leaves that depend on braces
Depending on how the version constraint on braces
is declared* in the package.json
file of modules that directly depend on braces
(leaves that were to the immediate left of braces
in the yarn why
output), they (or newer versions of them) may be able to resolve braces
to version 3.0.3
.
*For more information on dependency version constraints in package.json
check out this article.
Case 1: All the package.json
files for modules that depend on braces
, we see "braces": "^3.0.0"
. This makes us π
(relieved)! This means we can simply delete the 2 entries for braces
for our yarn.lock
file and then run yarn install
. This will install braces@3.0.3
and set it as the version that is depended on.
Case 2: Not all current modules depending on braces
can resolve their versions to 3.0.3
. We must then see if newer versions of these modules are available that update their braces
version constraint to be something that can resolve to 3.0.3
. If not, we're a bit stuck. We should create an issue on the module(s) repo for their maintainer(s) and then proceed to #2.
2. βοΈ Upgrade top-level that include braces
in their dependency sub-tree
In the case we've been looking at, we can see what top-most dependencies requires braces and can upgrade them. This will result in that package's entire sub-dependency tree getting updated as well. This can be helpful as it may remove the dependency on braces
entirely in some cases! If whatever module that depended on braces
can be resolved to braces@3.0.3
, that version will be installed and an update to yarn.lock
will have been made. This process must be repeated for all top-level dependencies that have a sub-tree that has a braces
leaf in it (the left-most packages in the output in step 2).
If, after completing this, your yarn.lock
file only contains a single braces@
entry with its version
set to 3.0.3
, AND your project is stable (you've not broken something by upgrading top-level dependencies), commit your change-set, rebuild your project and deploy!! β
If you still have yarn.lock
entry for braces
<= 3.0.2 then proceed to #3.
3. π¬ Add a "resolutions" entry in package.json
β οΈThis should only be done if braces
cannot be resolved to version 3.0.3
for some reason. For more things to consider, refer to the yarn
"Selective dependency resolutions" documentation for "Why would you want to do this?". Once you're confident this is the only way to proceed, follow the "How to use it?" instructions.