Managing packages with LernaJS

Multiple packages? Lerna comes to rescue!

Hi, it’s been a while since my last post. Today I’d like to share my experience using LernaJS to maintain create-react-app-express.

So what is LernaJS?

A tool for managing JavaScript projects with multiple packages.

So basically, Lerna is a tool that can help you with bootstraping, building, cleaning, and releasing your packages, and yes, it can do more than that.

I started create-react-app-express as a single package, and then grows into multiple packages (now it’s cra-universal). At first, I do it manually, like npm linking, building, cleaning, and releasing. I realize that some commands are actually similar between packages, like cleaning, running test, build. What if the tasks can be defined once in parent and can be run for all the packages? Lerna comes to rescue!

Install Lerna

Let’s install Lerna CLI:

# Using yarn
yarn global add lerna
# or npm
npm i -g lerna

You can initialize Lerna config by running:

lerna init

You’ll get lerna.json file and packages/ folder in your project folder.

lerna-repo/
packages/
package.json
lerna.json

You can now start creating your packages inside the packages/ folder.

Lerna usual commands

Now you can open your root package.json (lerna-repo/package.json) and insert some lerna commands:

"scripts": {
  "bootstrap": "lerna bootstrap",
  "test": "lerna run test:ci",
  "clean": "lerna clean",
  "clean:build": "lerna exec -- rimraf lib",
  "release": "lerna publish",
  "ls": "lerna ls"
},

You can use bootstrap for installing your packages dependencies and create symlinks. Suppose you have package core and server , and server depends on core , Lerna will symlink the package instead of installing the real one on registry.

You can use run for running specific npm script on your packages, and will skip ones that don’t have the command. So if you have “test:ci” script on package A, B, but not C, then it’ll only run on package A and B. The cool thing is, you just need to install Jest on the root package and it can be used on all the packages. You can also add --scope argument if you only want to run on some packages.

You can use exec for running a shell command on all your packages, similar with run command, but you don’t need to define the script on all packages! As you see in the script snippet above, we can install rimraf on the root, and define the clean:build command and it’ll run on all the packages.

You can use clean for removing node_modules on your packages.

You can use publish for publishing all your packages, including git tagging, and selecting semantic versioning update, and publishing to npm registry.

When publishing, Lerna will also update the package version on the package that depends on it. So if package A got a new version, let’s say 1.1.0, and package B depends on it as “^1.0.0”, it will be updated as “^1.1.0” automatically.

You can use ls for listing all your packages and their current version.

Bonus

Lerna bootstrap also rearranges your package’s dependencies order so you don’t need to do so manually😄

My takeaway

Personally I like the run and exec command, now you can have scripts that can be reusable for all the packages (e.g. build, testing, cleaning, linting), and put its definition and dependencies on the root package while you can have normal script that belongs to specific packages (e.g. build step that’s different for each package).

You can take a look at my root scripts here for example.

I think that’s all from me, if you’ve another good tips using Lerna, please comment below!

Thank you for reading.