What is a mono-repository and why you should try Lerna
Tags: lerna, gatsby, npm, yarnworkspaces
Published: 2019-07-10 17:39:25

---
title: What is a mono-repository and why you should try Lerna
published: true
description: Learn how to set up a Gatsby Theme project with Lerna and NPM: This guide will help you understand what is a mono-repository and how to use Lerna.
tags: Lerna, gatsby, npm, yarn workspaces 
cover_image: https://thepracticaldev.s3.amazonaws.com/i/bn3lyuy1smf8wyue0oru.png
---

### Learn how to set up a Gatsby Theme Project with Lerna: this guide will help you understand what is a mono-repo and how to manage it with Lerna using NPM instead of Yarn Workspaces.

## Index

- [What is Lerna and why it's worth a try?](#what-is-lerna-and-why-its-worth-a-try)
- [Setting Lerna with NPM from the shell](#setting-lerna-with-npm-from-the-shell)
- [Scaffolding folders and files with shell](#scaffolding-folders-and-files-with-shell)
- [Using Lerna](#using-lerna)
	+ [Choose your approach](#choose-your-approach)
- [Orchestrate everything with Lerna bootstrap](#orchestrate-everything-with-lerna-bootstrap)
- [Run commands and scripts](#run-commands-and-scripts)
- [Update Wizard](#update-wizard)



## What is Lerna and why it's worth a try?

I started to follow the [Gatsby Theme Authoring course](https://egghead.io/courses/gatsby-theme-authoring) from Jason Lengstorf (@jlengstorf) and soon realized that I don't know nothing about 'Yarn Workspaces'. It's a pretty way to manage what's called a mono-repo: a repository that has more than one project inside and could share dependencies.

By doppelmutzi from [doppelmutzi.github.io](https://doppelmutzi.github.io/monorepo-lerna-yarn-workspaces/):

> Such projects are called workspaces or packages. In contrast, using multiple repositories with each repository housing only one project is called a Multi-Repo approach. [...] 

One thing that amazed me with the mono-repo approach was that my *Packages/Workspaces* can locally share the same `node_modules` dependencies. This saves me time and physical space.

> lerna and yarn workspaces give us the ability to build libraries and apps in a single repo without forcing us to publish to npm or other registries. The beauty behind these technologies is that they can find package dependencies by analyzing package.json files located at each project’s root folder. Thereby, these tools make it obsolete to manually create symlinks or use "low-level" npm link directly.

Now, even if you are already using `yarn workspaces` I think Lerna worth the try because it provides a series of vantages. Besides you could use Lerna with Yarn Workspaces:

> with one lerna command you can iterate through all or particular packages, running a series of operations (such as linting, testing, and building) on each package. Thereby, it compliments yarn workspaces that takes over the dependency management process.

So finally I decided to learn Lerna - sorry for the tongue twister  - but I didn't want to switch to Yarn (since I'm just comfortable with npm). Here's how I managed to make it work:

## Setting Lerna with NPM from the shell

Create a directory for your project and enter on it with this command:

```shell
mkdir try-lerna
cd try-lerna
```

You can install Lerna globally (`npm i -g lerna`) or use it with npx.

- Init lerna

		npx lerna init --independent

This will:
- `init` a git repository 
- create a `package.json`
- create a `lerna.json` 
- create a `packages` folder (here you can place your workspaces). 

> **Packages** is how lerna call _workspaces_.
  
You can arrange the *Packages* as you want, if you prefer you can put all the packages in the root folder and then change the array in order to match (like a "yarn workspaces" approach).

My **lerna.json** file has `stream: true` (in order to see gatsby output when run develop with lerna) and `hoist: true` (in order to *hoist* node_modules also with the command `lerna add`; I'll explain this last feature forward):

```json
{
	"packages":["packages/*"],
	"version":"independent",
	"stream":true,
	"command":{
		"bootstrap":{
			"hoist":true
		}
	}
}
```

You can add this manually or use this simple shell command: 

```bash 
echo "{\r\t\"packages\":[\"packages/*\"],\r\t\"version\":\"independent\",\r\t\"stream\":true,\r\t\"command\":{\r\t\t\"bootstrap\":{\r\t\t\t\"hoist\":true\r\t\t}\r\t}\r}" > lerna.json       
```

## Scaffolding folders and files with shell

This section can be done by hand following the first lesson from Jason Lengstorf's course linked in the introduction.
I just collect all the basic passages in order to accomplish them with few commands from the shell.  
In case you want to use my approach remember to replace "NAME" with the name of your theme and then just copy and paste this commands:

```bash
mkdir -p packages/{gatsby-theme-NAME,site}
```
```bash
touch packages/{gatsby-theme-NAME,site}/{index.js,package.json}
```
```bash
echo "//loop" >> packages/{gatsby-theme-NAME,site}/index.js
```

Initial content for package.json on the site folder:

```bash
echo "{\r\t\"private\":true,\r\t\"name\":\"site\",\r\t\"version\":\"1.0.0\",\r\t\"license\":\"MIT\",\r\t\"scripts\":{\r\t\t\"builds\":\"gatsby build\",\r\t\t\"develop\":\"gatsby develop\",\r\t\t\"clean\":\"gaysby clean\"\r\t}\r}" >> packages/site/package.json    
```
package.json on the folder theme (replace NAME again and also insert YOUR_NPM_USER):

```bash
echo "{\r\t\"name\": \"@YOUR_NPM_USER/gatsby-theme-NAME\",\r\t\"version\": \"1.0.1\",\r\t\"license\": \"MIT\",\r\t\"main\": \"index.js\",\r\t\"scripts\": {\r\t\t\"builds\": \"gatsby build\",\r\t\t\"develop\": \"gatsby develop\",\r\t\t\"clean\": \"gaysby clean\"\r\t}\r}" >> packages/gatsby-theme-NAME/package.json    
```
Here's the result:

	├── packages
	│  ├── site
	│  └── gatsby-theme-NAME
	├── .gitignore
	├── package.json
	└── lerna.json

PS: I advice you to create a `.gitignore` and add `node_modules` right away:

```bash
echo "node_modules" > .gitignore
```

## Using Lerna

One problem with Lerna is that you can't install multiple dependencies per Package with one single command (like you do with `yarn workspace name_workspace add package1 package2 etc`). You need to do it one by one, like this (if you didn't install it globally you'll have to precede every command with `npx`): 

```bash
lerna add gatsby --scope site 
```
```bash
lerna add react --scope site
```
```bash
lerna add react-dom --scope site
```

Here `--scope site` is used to tell Lerna to install only in the Package "site".

However, you can install a dependency, let's say `gatsby`, for every *Package* with just one command:

	lerna add gatsby

This command will: 
- Install Gatsby for every Package (so in this case for Site and also for the Theme Package). 
- If you enabled `hoist` on the lerna.json it will create a node_modules folder in your root (see 'Lerna Bootstrap forward'):

#### Choose your approach
If you like this approach you can continue installing all the common dependencies (`react`, `react-dom`) on the Packages. If you need to install some dependencies only for one Package use `--scope name_of_the_package` (remember it's the name specified on the package.json not the name of the folder). 

You could also use npm by installing every dependency you want in the respective Package. But remember this approach will install a node_modules dependencies for every respective folder. This could be clean and then hoisted afterwards. Although this option is valid it will take you more time.

Whatever option you choose remember to add/link the local Theme Package as a dependency for the Site Package (use the name you set on `package.json`):


	lerna add @YOUR_NPM_USER/gatsby-theme-NAME --scope=site

    
## Orchestrate everything with Lerna bootstrap 

In the end you can safely do: 

	lerna bootstrap 

This command is really helpful, specially if you already have your package.json filled with their respective dependencies. 

With `hoist: true` option (on `lerna.json`) it will place a shared `node_modules` folder located in the root, then it will link local dependencies in their respective `package.json`. 

I really like [--hoist feature](https://github.com/lerna/lerna/tree/master/commands/bootstrap#--hoist-glob) it's a process that avoid duplicated dependencies on the node_modules, so it saves physical space and installation time.

If for whatever reason you want to clean anything first you could do:

    lerna clean && lerna link && lerna bootstrap 

## Run commands and scripts

To run scripts (placed on the `package.json`) you need to specify (or not) the target Package:

	lerna run develop --scope Package_name

Lerna also let you run any commands from the root to every Package (use dash). For example:

	lerna exec -- rm -rf node_modules 

This will remove node modules on every Package folder.

## Update Wizard 

In the future, when you'll need to update your packages you can use the [update-wizard](https://www.npmjs.com/package/lerna-update-wizard):

	npm install --save-dev lerna-update-wizard

then you'll need to run only this command and the wizard will help you: 

	lernaupdate