What is a lernaJs

LernaJs is a multi-package management tool developed by the Babel team. Because Babel contains many sub-packages, which used to be stored in multiple repositories, it is difficult to manage, especially when there is a call to the system package, which is difficult to publish. So in order to manage packages better and faster,Babel introduced lernaJs, which uses monorepo concepts. Now React,Babel,Angular, and Jest all use this tool to manage packages.

Lerna official website: lerna.js.org/ Lerna Warehouse: github.com/lerna/lerna…

What is a monorepo?

Monorepo is compared to multi-package. Multi-package is the creation of multiple repositories, one for each package. Monorepo creates a repository where multiple packages are managed, which has two benefits:

  1. The communication between various packages is more convenient. For multi-package, if one of the packages in the system is modified, the version needs to be issued separately, and other packages referencing this package need to be issued. With Lerna you can automatically manage the release of these packages, which is very convenient
  2. Some common configurations, such as ESLint, Babel,rollup, etc., manage these development configurations uniformly

How to set up the environment

Install and initialize the LERNA project

Install lernajs

npm install lerna -g
Copy the code

Initialize the lernaJs project

lerna init
Copy the code

After execution, a MonorePO managed by Lerna is automatically generated. Its file structure is as follows:

Package. json // Lerna JS configuration fileCopy the code

Add child package

After the environment is initialized, you need to add a subpackage first. The command to add a subpackage is as follows:

lerna create <pkgName>
Copy the code

After executing this command, the package will ask the same questions about the package name, version and so on as NPM init. After filling in these questions, the package will automatically create a subpackage. The default directory structure for subpackages is as follows:

<packName> __tests__ // Test file lib // The entry file for the package is in the package.json readme.md directory by defaultCopy the code

How do I add dependencies to subpackages

The command to add a dependency is:

Lerna add <moduleName> --scope = <pkgName> --scope = <pkgName2> // Add pkgName1 to pkgName2. Cross-references within packages copy pkgName1 to pkgName2Copy the code

All subpackages update dependencies

lerna bootstrap
Copy the code

How to package

Since only pure JS files need to be packaged, I chose rollup here, and the configuration idea of WebPack is the same. Let’s talk about what we want to achieve.

  • The SRC /index.js file for each subpackage is the entry file for the source code
  • Lib /index.js for each subpackage is the packaged file and the entry point to which the package is referenced
  • The configuration of each subpackage is basically the same, such as with Babel transformation, which compresses the code
  • NPM run Build packName Packs the specified package
  • NPM run dev packName Develops the specified package

Start by creating a script directory in the root directory that contains configuration files for development and packaging. Then create a build.js file under script to configure rollup. In build.js, the rollup javascript API is used. Rollup configurations can be found on the rollup website at www.rollupjs.org

Execute the following command to install the necessary packages:

npm i rollup rollup-plugin-babel rollup-plugin-commonjs rollup-plugin-terser --save-dev
Copy the code

The main code for build.js is as follows:

const rollup = require('rollup'); Rollup const terser =require('rollup-plugin-terser').terser // plugin for compressed code const commonjs = require('rollup-plugin-commonjs'Const Babel = require(const Babel = require(const Babel = require(const Babel = require('rollup-plugin-babel'Argv [2] const args = process.argv[2] const projectPath = `./packages/${args}Const inputOptions = {input: 'const inputOptions = {input:'${projectPath}/ SRC /index.js', plugins: [Babel ({// Babel file Settings, babel.config.js file Settings, runtimeHelpers:true,
      exclude: 'node_modules/**'}), commonjs(), terser() ] }; // Const outputOptions = {file: '${projectPath}/lib/index.js`,
  format: 'esm', // The output mode is es6 mode name: '${args}'// output a reference name called package}; asyncfunction build() { // create a bundle const bundle = await rollup.rollup(inputOptions); // inputOptions is placed here console.log(bundle.watchfiles); // an array of file names this bundle depends on await bundle.write(outputOptions); } build();Copy the code

After editing the file, add commands under scripts of package.json to execute the build file

"scripts": {
    "build": "node script/build.js",}Copy the code

How to test

The test uses Facebook’s JEST framework, and has the following requirements for the test:

  • NPM Run test packName Runs all tests under this subpackage
  • NPM run test packName testFileName Because each subpackage contains multiple test files, this command is used to run the test files in the subpackage

As with rollup configuration above, jest’s javascript API is required. Add the test.js file under script

Const rawArgs = process.argv[2] // Get the package name consttestFile = process.argv[3]|| ' '// get test filename const path = require('path')
let rootDir = path.resolve(__dirname, '.. / ')


rootDir = rootDir + '\\packages\\'Const jestArgs = [const jestArgs = [const jestArgs = ['--runInBand'.'--rootDir', rootDir, // Incoming package pathtestFile? `${testFile}.spec.js`:' 'Console. log(' \n===> RUNNING: jest${jestArgs.join(' ')}`)

require('jest'). The run (jestArgs) / / executionCopy the code

Then add the following script to package.json:

"scripts": {
    "test": "node script/test.js"
}
Copy the code

Add ESLint and CommitLint

Adding ESLint and COMMITLint is easier here, but because of the Monorepo structure, it is good to do the ESLint and COMMITLint configuration once, and all subpackages will use this configuration.

How to publish

The order issued was

lerna publish
Copy the code

You can set rules for version numbers in lerna.json

// lerna.json

"version": "0.0.3"// If it is a number, all subpackages are of this version. Set to 'independent' to manage the version of each package independentlyCopy the code

Lernajs compares package changes and automatically publishes the subpackages that need to be published.

Automatically adds CHANGELOG

Add the following Settings to lerna.json:

// lerna.json

"command": {
  "publish": {
    "conventionalCommits": true}}Copy the code

This will automatically add CHANGELOG at release time

conclusion

This allows us to implement the structure of a multi-package Monorepo, each of which can be developed, packaged, and tested by command. The configuration of ESLint, Babel, and Jest is the same for all subpackages. You can easily issue a command, update a command and add dependencies.

Final file structure

docs
|---README.md
packages
|---packageOne
|---packageTwo
      .
      .
      .
script
|---build.js
|---test.js
.commitlintrc.js
.eslintignore
.eslintrc
.gitignore
babel.config.js
lerna.json
package.json
Copy the code

Using the command

Commands about packages

Create a new package:

lerna creat <packname>
Copy the code

release

lerna publish
Copy the code

All packages update node_modules

lerna bootstrap
Copy the code

Add package B to package A

lerna add pack-B --scope = pack-A
Copy the code

Commands about development

Build one of the packages:

npm run build <packname>
Copy the code

Test one of the packages

npm run test <packname>
Copy the code

Develop one of the packages

npm run dev <packname>
Copy the code