Frontend Development 19 min read

Using Lerna to Manage a JavaScript Monorepo: Workflow, Configuration, and Best Practices

Using Lerna, the article shows how to convert a multi‑package JavaScript project into a production‑ready monorepo by initializing Lerna, hoisting dependencies, automating versioning and publishing, integrating commit‑linting, code‑style tools, Babel builds, and VSCode debugging for streamlined development.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Using Lerna to Manage a JavaScript Monorepo: Workflow, Configuration, and Best Practices

For developers who maintain multiple npm packages, a common dilemma is whether to keep all packages in a single repository or to manage each in its own repository. This article demonstrates, through a concrete example, how to use Lerna to manage multiple packages, integrate it with other tools, and establish an efficient, production‑ready workflow.

Background

The project maintains a CLI that is published to npm. The repository contains three sub‑folders (pkg‑a, pkg‑b, pkg‑main) each representing a package. The build and release process involves compiling each package with webpack, Babel, and UglifyJS, then copying the compiled files into a final pkg‑npm folder for publishing.

Pain points

Debugging is difficult because the final bundle is assembled from compressed files.

Package dependencies are unclear; pkg‑a and pkg‑b have no versioning, while pkg‑main’s package.json is copied into pkg‑npm and still depends on pkg‑a and pkg‑b.

Redundant dependencies: each package installs its own copy of Babel, webpack, etc.

Version numbers must be updated manually for the combined package.

No CHANGELOG.md because pkg‑a and pkg‑b lack proper version management.

These issues indicate that the project is effectively a monorepo that has not been managed as such.

Monorepo vs. Multirepo

A monorepo (monolithic repository) stores all related packages in a single repository, allowing each package to be published independently. A multirepo uses a separate repository for each package. Monorepos enable unified tooling, easier cross‑package debugging, and reduced coordination overhead.

Lerna Overview

Lerna is a widely adopted tool for managing JavaScript projects with multiple packages. It optimizes workflows around git and npm, handling inter‑package dependencies and automating versioning and publishing.

Installation

npm i -g lerna

Project Initialization

lerna init

The generated package.json and lerna.json look like:

{
  "name": "root",
  "private": true,
  "devDependencies": {
    "lerna": "^3.15.0"
  }
}

{
  "packages": ["packages/*"],
  "version": "0.0.0"
}

Adding Packages

lerna create @mo-demo/cli
lerna create @mo-demo/cli-shared-utils

Adding Dependencies

lerna add chalk                     // add to all packages
lerna add semver --scope @mo-demo/cli-shared-utils   // add to a specific package
lerna add @mo-demo/cli-shared-utils --scope @mo-demo/cli   // internal package dependency

Publishing

lerna publish

Lerna will prompt for the version (e.g., 0.0.1‑alpha.0 ) and handle version bumps, tag creation, and npm publishing.

Bootstrap with Hoisting

To avoid duplicated node_modules across packages, use hoisting:

lerna bootstrap --hoist

Hoisting can be made default by adding to lerna.json :

{
  "packages": ["packages/*"],
  "command": {
    "bootstrap": {"hoist": true},
    "version": {"conventionalCommits": true}
  },
  "ignoreChanges": ["**/*.md"],
  "version": "0.0.1-alpha.1"
}

Commit Workflow

Use commitizen and cz-lerna-changelog to enforce conventional commit messages:

npm i -D commitizen cz-lerna-changelog

Configure in package.json :

{
  "scripts": {"c": "git-cz"},
  "config": {"commitizen": {"path": "./node_modules/cz-lerna-changelog"}}
}

Validate commits with commitlint and husky :

npm i -D @commitlint/cli @commitlint/config-conventional husky
// commitlint.config.js
module.exports = {extends: ['@commitlint/config-conventional']};
// package.json snippet
"husky": {"hooks": {"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}}

Enforce code style with standardjs and lint‑staged :

npm i -D standard lint-staged
// package.json snippet
"lint-staged": {"*.js": ["standard --fix", "git add"]},
"husky": {"hooks": {"pre-commit": "lint-staged"}}

Babel Configuration

// babel.config.js
module.exports = function (api) {
  api.cache(true);
  const presets = [
    ['@babel/env', {targets: {node: '8.9'}}]
  ];
  if (!process.env['LOCAL_DEBUG']) {
    presets.push(['minify']);
  }
  return {presets, plugins: [], ignore: ['node_modules']};
};

Package Scripts

{
  "scripts": {
    "c": "git-cz",
    "i": "lerna bootstrap",
    "u": "lerna clean",
    "p": "npm run b && lerna publish",
    "b": "lerna exec -- babel src -d dist --config-file ../../babel.config.js"
  }
}

Debugging

Use VSCode launch configuration to debug the CLI in source mode:

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [{
    "type": "node",
    "request": "launch",
    "name": "debug cli",
    "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/babel-node",
    "runtimeArgs": ["${workspaceRoot}/packages/cli/src/index.js"],
    "env": {"LOCAL_DEBUG": "true"},
    "console": "integratedTerminal"
  }]
}

With the LOCAL_DEBUG flag, the entry files load the source code ( src ) instead of the compiled dist , enabling step‑by‑step debugging across package boundaries.

Conclusion

The described setup provides a complete, production‑ready monorepo workflow using Lerna, covering package management, unified build configuration, automated versioning, conventional commits, code style enforcement, and seamless debugging. It dramatically improves development efficiency and reduces maintenance overhead.

JavaScriptBuild AutomationmonorepogitCIlernaPackage Management
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.