Guide to NPM and Lerna

Status: ideation
Share: Twitter

#Tools

  • npm for package management
  • Lerna for managing project packages/workspace definition

#Some Good Guidelines

  • Install non-cli devDependencies in the project root
  • Install cli devDependencies (such as Babel or Webpack) in the project root
  • Install dependencies at the package root
  • Babel should use ~babel src -d lib --root-mode upward~ so that it knows to look at the root-level babel.config.js

#Gotchas

  • For Jest, I had to add babel.config.js to each package and module.exports a require of the root babel.config.js to make Jest happy. This feels...weird. I'm pretty sure I can create a transformer manually in the jest.config.js, but I didn't try that yet.

Think about it as if you were to split a package out into its own thing and never have to maintain it again. Its dependencies should always be installable but the devDependencies shouldn't be necessary per-package.

#Up & Running

Initialize the repository:

npm init -y

Install Lerna to the project root:

npm install lerna --save-dev

Initialize the project as a lerna project:

npx lerna init

This will scaffold out a =lerna.json= that looks like this:

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

Create your first package:

npx lerna create @orglnd/org-parse

Which creates a package in =packages/org-parse= with a =package.json= with the following:

{
"name": "@orglnd/org-parse",
"version": "0.0.0",
"description": "parser for org-mode",
"keywords": [
"org-mode"
],
"author": "Chase Adams [realchaseadams@gmail.com](mailto:realchaseadams@gmail.com)",
"homepage": "",
"license": "MIT",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "**tests**"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}

=publishConfig= is important because it allows =lerna= to create root level package releases.

In this project we'll be using the following tools that need to be installed at the project root:

  • babel
  • typescript
  • jest
  • prettier
  • eslint
npm install --save-dev \
@babel/cli \
@babel/core \
@babel/preset-env \
@babel/preset-react \
@babel/preset-typescript \
@babel/plugin-transform-typescript \
@babel/plugin-transform-modules-commonjs \
@babel/plugin-proposal-export-default-from \
@babel/plugin-proposal-export-namespace-from \
@babel/plugin-proposal-object-rest-spread \
prettier \
eslint \
@typescript-eslint/parser \
@typescript-eslint/eslint-plugin \
eslint-config-prettier \
eslint-plugin-prettier \
typescript \
@types/node \
jest \
@types/jest \
babel-jest

Making Jest work with Typescript and Babel, add this to the root level =jest.config.js=:

module.exports = {
testMatch: [`**/*/__tests__/**/*.ts?(x)`],
transform: {
"^.+\\.tsx?$": `babel-jest`,
},
};

I use the same VS Code for Flow as I do for this TypeScript application. I have typescript validation disabled by default and enable it for this specific project in =.vscode/settings.json= with the following config:

{
"typescript.validate.enable": true
}

Installing a package (example is =@babel/cli=) to all workspaces:` yarn lerna add @babel/cli

Installing a package to one workspace:```
yarn lerna add unified --scope @chaseadamsio/markdown-to-org
Did you enjoy this article? Share it on Twitter!
chaseadams.io is powered by GatsbyJS, GitHub & Netlify.
Deployed commit of chaseadams.io is a4a859
👋 Say Hi!