DEV Community

Dario
Dario

Posted on • Updated on

Why should you use yarn workspaces?

The Problem

If you are working on a non-trivial project, chances are that at some point you will decide to extract pieces of code into separate packages.

Then you'll have some of your packages depending on others, and dependencies common to several packages, for example, lodash might be used by several packages.

Each of your packages will have its own node_modules. The same version of lodash can be repeated several times.

When you change the code of a package, you'll need to bump its version number, publish the changes, go to every other package that depends on it, update the version number of the dependency, and upgrade.

The Solution

Yarn workspaces helps with the two issues:

  • It sets up a single node_modules without repetitions.
  • It allows you to change the code of one of your packages and have those changes instantly visible to the other packages that use it.

Example

Say we have two packages: conversions and myapp.

conversions/package.json looks like this:

{
  "name": "conversions",
  "version": "0.1.0",
  "main": "index.js"
}
Enter fullscreen mode Exit fullscreen mode

conversions/index.js like this:

const milesToKilometers = miles => miles * 1.609344;

module.exports = { milesToKilometers };
Enter fullscreen mode Exit fullscreen mode

myapp/package.json like this:

{
  "name": "myapp",
  "version": "0.4.3",
  "main": "index.js",
  "dependencies": {
    "conversions": "^0.1.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

And finally myapp/index.js like this:

const { milesToKilometers } = require('conversions');

console.log(`3 miles = ${milesToKilometers(3)}`);
Enter fullscreen mode Exit fullscreen mode

Inside our top-level directory, let's create a sub-directory for holding all of our packages:

mkdir packages
Enter fullscreen mode Exit fullscreen mode

And a package.json with the content:

{
  "name": "root",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}
Enter fullscreen mode Exit fullscreen mode

private: true, because this root package is not meant to be published.

workspaces: ["packages/*"], to tell yarn where to look for our packages.

Now lets move all your packages to the packages directory.

mv conversions packages/
mv myapp packages/
Enter fullscreen mode Exit fullscreen mode

After moving, our directory layout looks like this:

/packages
  /conversions
    /node_modules
    index.js
    package.json
  /myapp
    /node_modules
    index.js
    package.json
  package.json  
Enter fullscreen mode Exit fullscreen mode

Now lets go ahead and delete the individual package's node_modules:

rm -rf packages/conversions/node_modules
rm -rf packages/myapp/node_modules
Enter fullscreen mode Exit fullscreen mode

And tell yarn to do its thing, simply by running:

yarn
Enter fullscreen mode Exit fullscreen mode

You'll notice the updated layout:

/packages
  /conversions
    index.js
    package.json
  /myapp
    index.js
    package.json
  /node_modules
  package.json  
Enter fullscreen mode Exit fullscreen mode

Ok, time to try it.

Lets change packages/conversions/index.js to:

const ONE_MILE_IN_KILOMETERS = 1.609344;

const milesToKilometers = miles => miles * ONE_MILE_IN_KILOMETERS;

const kilometersToMiles = kilometers => kilometers / ONE_MILE_IN_KILOMETERS;

module.exports = { milesToKilometers, kilometersToMiles };
Enter fullscreen mode Exit fullscreen mode

The new function kilometersToMiles should be instantly available to be used in packages/myapp/index.js:

const { milesToKilometers, kilometersToMiles } = require('conversions');

console.log(`3 miles = ${milesToKilometers(3)}`);
console.log(`8 kms = ${kilometersToMiles(8)}`);
Enter fullscreen mode Exit fullscreen mode

Notice that we didn't changed the conversions package version number. We will still need to do it later in order to publish the updated version of this package, but we don't have to do it during development just to have those changes available to myapp .


You may find the code of this post here.


If you liked what you've read and want to get notified when I publish something new, please consider subscribing to my mailing list.

Top comments (0)