什么是lernaJs
lernaJs是由Babel团队出的一个多包管理工具。因为Babel包含很多子包,以前都是放在多个仓库里的,管理比较困难,特别是有调用系统内包的时候,发布比较麻烦。所以为了能更好更快的夸包管理,babel推出了lernaJs,使用了monorepo的概念,现在React,Babel,Angular,Jest都在使用这个工具来管理包。
lerna官网: lerna.js.org/
lerna仓库: github.com/lerna/lerna…
什么是monorepo?
monorepo是比较于multi-package的。multi-package就是建立多个仓库,每个包建立一个仓库。而monorepo是建立一个仓库,多个包都在这个仓库中管理,这样可以有两个好处:
- 各个包之间的沟通更加的方便,如果multi-package的话,系统内其中一个包修改,需要单独发版,而且引用这个包的其他包都需要发版。使用lerna的话可以自动管理这些包的发版,很方便
- 一些可以共用的配置,比如eslint,babel,rollup等,可以统一的管理这些开发配置
如何搭建环境
安装和初始化lerna项目
安装lernajs
npm install lerna -g
初始化lernaJs项目
lerna init
执行后,会自动生成一个由lerna管理的monorepo。 其文件结构如下:
lerna-repo/
packages/ // 子包都放在这个目录中
package.json
lerna.json // lerna js的配置文件
添加子包
环境初始好以后,首先需要添加一个子包,添加子包的命令如下:
lerna create <pkgName>
执行这个命令后,会问一些包名啊,版本等问题和执行npm init后问的差不多,填完这些问题后包就会自动创建一个子包。 子包默认的目录结构如下:
<packName>
__tests__ // 测试文件
lib // 包的入口文件默认在这个目录下
package.json
README.md
怎么给子包添加依赖
添加依赖的命令是:
lerna add <moduleName> // 所有子包都添加这个依赖
lerna add <moduleName> --scope = <pkgName> // 给scope后的包添加依赖
lerna add <pkgName1> --scope = <pkgName2> // 给pkgName2中添加pkgName1,包内的互相引用,会复制pkgName1到pkgName2中
所有子包更新依赖
lerna bootstrap
如何打包
因为需要打包的只是纯js文件,我这里选择了rollup,webpack的配置思路是一样的。 先介绍一下想实现的效果。
- 每个子包的src/index.js是源代码的入口文件
- 每个子包的lib/index.js是打包后的文件,也是这个包引用时的入口
- 每个子包的配置是基本一样的,比如用babel转换,都会压缩代码
- npm run build packName 打包指定的包
- npm run dev packName 开发指定的包
先在根目录建立一个script目录,该目录下会包含一些开发和打包需要的配置文件。之后在script下建一个build.js,用来配置rollup。 在build.js中,要用到rollup的javascript api。关于rollup的配置可以看rollup的官网: www.rollupjs.org
执行以下命令安装必要的包:
npm i rollup rollup-plugin-babel rollup-plugin-commonjs rollup-plugin-terser --save-dev
build.js的主要代码如下:
const rollup = require('rollup'); // 引入rollup
const terser =require('rollup-plugin-terser').terser // 压缩代码的插件
const commonjs = require('rollup-plugin-commonjs') // rollup默认支持es6的模块系统,需要支持commonjs的话需要这个插件
const babel = require('rollup-plugin-babel') // rollup的babel 插件
const args = process.argv[2] // 拿到 npm run build packName 中的packName
const projectPath = `./packages/${args}` // 子包所在的路劲
// 输入的文件配置
const inputOptions = {
input: `${projectPath}/src/index.js`,
plugins: [
babel({ // babel文件的设置,会读取根目录的babel.config.js文件配置
runtimeHelpers: true,
exclude: 'node_modules/**'
}),
commonjs(),
terser()
]
};
// 输出的配置
const outputOptions = {
file: `${projectPath}/lib/index.js`,
format: 'esm', // 引出的方式为es6的方式
name: `${args}` // 输出可引用名为package的名字
};
async function build() {
// create a bundle
const bundle = await rollup.rollup(inputOptions); // inputOptions放在这里
console.log(bundle.watchFiles); // an array of file names this bundle depends on
await bundle.write(outputOptions); // outputOptions放在这里
}
build();
编辑完这个文件后,可在package.json的scripts下添加命令,来执行这个build文件
"scripts": {
"build": "node script/build.js",
}
如何测试
测试选用了facebook的jest框架,对于测试有以下要求:
- npm run test packName 运行这个子包下的所有测试
- npm run test packName testFileName 因为每个子包包含多个测试文件,这个命令用来运行这个包下的测试文件
和上面配置rollup一样,也需要用jest的javascript api。 在script下添加test.js文件
const rawArgs = process.argv[2] // 获取包名
const testFile = process.argv[3]|| '' // 获取测试文件名
const path = require('path')
let rootDir = path.resolve(__dirname, '../')
rootDir = rootDir + '\\packages\\' + rawArgs // 拼出包的路劲
const jestArgs = [
'--runInBand',
'--rootDir', rootDir, // 传入包路径
testFile?`${testFile}.spec.js`:'' //
] // jest 的一些配置
console.log(`\n===> running: jest ${jestArgs.join(' ')}`)
require('jest').run(jestArgs) // 执行
然后在package.json加入以下script:
"scripts": {
"test": "node script/test.js"
}
添加eslint和commitlint
添加eslint和commitlint这里比较简单, 这里提一点就是因为Monorepo的结构,所以做一次eslint和commitlint的配置就好,所有的子包都会使用这个配置。
如何发布
发布的命令是
lerna publish
在lerna.json中可以设置版本号的规则
// lerna.json
"version": "0.0.3" // 如果是数字,就是所有子包都是这个版本。设置成‘independent’,独立管理每个包的版本
lernajs 会对比包的变化,自动发布需要发布的子包。
自动添加CHANGELOG
在lerna.json中添加如下设置:
// lerna.json
"command": {
"publish": {
"conventionalCommits": true
}
}
这样就能在发布时自动添加CHANGELOG了
总结
这样我们就实现了多包的monorepo的结构,可以通过命令开发,打包,测试其中的每个子包。所有子包的eslint,babel,jest的配置都是一样的。可以方便的一个命令发版,一个命令更新和添加依赖。
最终文件结构
docs
|---README.md
packages
|---packageOne
|---packageTwo
.
.
.
script
|---build.js
|---test.js
.commitlintrc.js
.eslintignore
.eslintrc
.gitignore
babel.config.js
lerna.json
package.json
使用命令
关于包的命令
新建一个包:
lerna creat <packname>
发布
lerna publish
所有包更新node_modules
lerna bootstrap
A包中添加B包
lerna add pack-B --scope = pack-A
关于开发的命令
构建其中一个包:
npm run build <packname>
测试其中一个包
npm run test <packname>
开发其中一个包
npm run dev <packname>