npm Blog (Archive)

The npm blog has been discontinued.

Updates from the npm team are now published on the GitHub Blog and the GitHub Changelog.

Monorepos and npm

Splitting a large, monolithic codebase into small, encapsulated modules is usually good practice from an architectural perspective. Modularization is useful for everything from microservices to libraries of reusable components. However, it can also be a nightmare from a publishing and source code management perspective.

Monorepos are a popular solution to this problem. Instead of one code repository per module, you put all modules in the same code repository. This “monorepo” is then the only thing a developer needs in order to do development with your application. Discovery, access control, and versioning are all simpler by virtue of everything being in the same place.

Obviously, you have to be okay with granting access to all of the code or none of the code. But if this is acceptable, monorepos can give you all the benefits of modularization with very straightforward source code management.

So how do monorepos work with npm? The two most popular solutions are Yarn workspaces and lerna. Since lerna works with npm, let’s take a look at how it implements monorepos.

First, let’s install lerna globally.

$ npm install -g lerna

Next, we need to create new lerna repository:

$ mkdir monorepo_example
$ cd monorepo_example
$ lerna init

If you look at the contents of lerna.json, you can see where the version and packages are defined.

$ cat lerna.json
{
  “packages”: [
    “packages/*”
  ],
  “version”: “0.0.0”
}

As seen above, the version for all of the packages in our monorepo is 0.0.0. However, there aren’t any packages in our monorepo yet. Before we add them, we should login to our registry so lerna sets the publishConfig correctly on each new package.

If you’re publishing somewhere other than the public npm registry (for example, npm Enterprise), you’ll first need to set your registry.

$ npm config set registry https://registry.npm.yourcompany.com

Next, we authenticate to the registry with a user that has publish permissions. If your packages won’t have a scope, you can omit the –scope flag.

$ npm login –scope test

We now add a few scoped packages to our monorepo.

$ lerna create @test/a
$ lerna create @test/b
$ lerna create @test/c

If you chose the default settings for each package, you’ll now have three packages (a, b, c) in the test scope with version 0.0.0. Before we can increment the version, we need to commit what we’ve got so far and create a remote for git:

$ git add .
$ git commit -m “Initial commit”
$ git remote add origin git@github.com:username/reponame.git
$ git push -u origin master

Now we can bump the version of all packages with one command.

$ lerna version major

This command not only bumps every package to 1.0.0, but also pushes the version update to git for you. The last step is to publish these packages to npm. Lerna lets us do this with a single command.

$ lerna publish from-git

If this command succeeded, you’ve successfully published all packages in your first monorepo to npm. Hooray!

In summary, we:

  1. Created a new monorepo with Lerna.
  2. Added three new scoped packages to our monorepo.
  3. Bumped the version of all packages and committed to git with a single command.
  4. Published all packages to an npm registry with a single command.

For more on Lerna, including their solution to the duplication problem (hoisting), I recommend their excellent docs at https://github.com/lerna/lerna.

We’re also excited to announce that we plan to bring first-class monorepo support to npm@7. If you use monorepos with npm, we need your feedback! Let us know what you like or dislike about your current monorepo solution.

Happy coding,

The team at npm, Inc.