- 关于lerna
 - 命令介绍
 - Adds the module-1 package to the packages in the ‘prefix-‘ prefixed folderslerna add module-1 packages/prefix-* # Install module-1 to module-2lerna add module-1 —scope=module-2 # Install module-1 to module-2 in devDependencieslerna add module-1 —scope=module-2 —dev # Install module-1 in all modules except module-1lerna add module-1 # Install babel-core in all moduleslerna add babel-core
 - DEMO
 - 参考:
 
关于lerna
Lerna 是一个用git和npm来优化这个管理多包库工作流的工具。用于管理具有多个包的JavaScript项目的工具。 这个介绍可以说很清晰了,引入lerna后,上面提到的问题不仅迎刃而解,更为开发人员提供了一种管理多packages javascript项目的方式。 1. 自动解决packages之间的依赖关系。 2. 通过git 检测文件改动,自动发布。 3. 根据git 提交记录,自动生成CHANGELOG
命令介绍
| api | 作用 | 
|---|---|
| lerna bootstrap | 安装依赖 | 
| lerna clean | 删除各个包下的node_modules | 
| lerna init | 创建新的lerna库 | 
| lerna list | 显示package列表 | 
| lerna changed | 显示自上次relase tag以来有修改的包, 选项通 list | 
| lerna diff | 显示自上次relase tag以来有修改的包的差异, 执行 git diff | 
| lerna exec | 在每个包目录下执行任意命令 | 
| lerna run | 执行每个包package.json中的脚本命令 | 
| lerna add | 添加一个包的版本为各个包的依赖 | 
| lerna import | 引入package | 
| lerna link | 链接互相引用的库 | 
| lerna create | 新建package | 
| lerna publish | 发布 | 
可以使用 lerna [command] -h 查找单个命令的使用方法和参数.
1、lerna bootstrap
执行该命令会做下面四件事情:
(1)、为每个包安装依赖
(2)、.链接相互依赖的库到具体的目录
(3)、.执行 npm run prepublish
(4)、执行 npm run prepare
默认是npm i,因为我们指定过yarn,so,run yarn install,会把所有包的依赖安装到根node_modules.
| – —production —no-optional | 指定npm client的参数 | 
| –hoist | 把依赖安装到根目录的node_modules | 
| –ignore | 忽略的包 —ignore test-* 忽略名称以test开头的包 | 
| –scope | 指定的包 参数的含义是指包的名称 | 
| –ignore-scripts | 不执行声明周期脚本命令, 比如 prepare | 
| –registry | 指定registry | 
| –npm-client | 指定安装用的npm client lerna bootstrap —npm-client=yarn | 
| –use-workspace | 使用yarn workspace, 没用过 | 
| –no-ci | 默认调用 npm ci 替换 npm install , 使用选项修改设置 npm ci 类似于 npm-install ,但它旨在用于自动化环境,如测试平台,持续集成和部署。 | 
| –skip-git | 将不会创建git commit或tag | 
| –skip-npm | 将不会把包publish到npm上 | 
| –canary | 可以用来独立发布每个commit,不打tag lerna publish —canary | 
2、lerna list 列举当前lerna 库包含的包
列出所有的包,如果与你文夹里面的不符,进入那个包运行yarn init -y解决
| –json | 显示为json格式 | 
| –all | 显示包含private的包 | 
| –long | 显示更多的扩展信息 | 
3、lerna changed
列出下次发版lerna publish 要更新的包。选项通 list
原理:
需要先git add,git commit 提交。
然后内部会运行git diff —name-only v版本号 ,搜集改动的包,就是下次要发布的。并不是网上人说的所有包都是同一个版全发布。
➜ lerna-repo git:(master) ✗ lerna changed info cli using local version of lernalerna notice cli v3.14.1lerna info Looking for changed packages since v0.1.4daybyday #只改过这一个 那下次publish将只上传这一个lerna success found 1 package ready to publish
4、lerna diff
显示自上次relase tag以来有修改的包的差异, 执行 git diff
5、lerna exec
在每个包目录下执行任意命令
| –concurrency | 默认命令时并行执行的, 我们可以设置并发量为1(全局参数) | lerna exec —concurrency 1 – ls -la | 
| –scope | 设置包含的package | lerna exec —scope my-component – ls -la | 
| –stream | 交叉并行输出结果 | lerna exec —stream – babel src -d lib | 
| –parallel | Execute command with unlimited concurrency, streaming prefixed output.指为项目的中需要一直的进程,打印所有子进程的输出。说得有点绕,理解为需要一直运行的就加上这个参数就可以了。 | |
| –no-bail | Continue executing command despite non-zero exit in a given | 
$ lerna exec — < command > [..args] # runs the command in all packages$ lerna exec — rm -rf ./node_modules$ lerna exec — protractor conf.jslerna exec —scope my-component — ls -la
6、lerna run 选项同lerna exec
执行每个包package.json中的脚本命令
lerna run < script > — [..args] # 运行所有包里面的有这个script的命令$ lerna run —scope my-component test
7、lerna init
创建一个新的lerna库或者是更新lerna版本
默认lerna有两种管理模式, 固定模式和独立模式
固定模式 —exact
固定模式,通过lerna.json的版本进行版本管理。当你执行lerna publish命令时, 如果距离上次发布只修改了一个模块,将会更新对应模块的版本到新的版本号,然后你可以只发布修改的库。
在publish的时候,会在lerna.json文件里面”version”: “0.1.5”,,依据这个号,进行增加,只选择一次,其他有改动的包自动更新版本号。
这种模式也是Babel使用的方式。如果你希望所有的版本一起变更, 可以更新minor版本号,这样会导致所有的模块都更新版本。
独立模式 —independent
独立模式,init的时候需要设置选项 —independent. 独立模式允许管理者对每个库单独改变版本号,每次发布的时候,你需要为每个改动的库指定版本号。这种情况下, lerna.json的版本号不会变化了, 默认为independent。
lerna.json文件里面”version”: “independent”,每次publish时,您都将得到一个提示符,提示每个已更改的包,以指定是补丁、次要更改、主要更改还是自定义更改。
8、lerna clean
9、lerna import
导入指定git仓库的包作为lerna管理的包
| –flatten | 如果有merge冲突, 用户可以使用这个选项所谓单独的commit | lerna import ~/Product —flatten | 
| –dest | 可以指定导入的目录(lerna.json中设定的目录) | lerna import ~/Product —dest=utilities | 
10、lerna add [@version] [—dev] [—exact] 
增加本地或者远程 package做为当前项目 packages里面的依赖
- —dev devDependencies 替代 dependencies
 - —exact 安装准确版本,就是安装的包版本前面不带^, Eg: “^2.20.0” ➜ “2.20.0”
```bash
Adds the module-1 package to the packages in the ‘prefix-‘ prefixed folderslerna add module-1 packages/prefix-* # Install module-1 to module-2lerna add module-1 —scope=module-2 # Install module-1 to module-2 in devDependencieslerna add module-1 —scope=module-2 —dev # Install module-1 in all modules except module-1lerna add module-1 # Install babel-core in all moduleslerna add babel-core
 
<a name="Y7e6w"></a>### 11、lerna link链接互相引用的库<br />链接依赖<br />有个强大的命令叫 `lerna link convert`,它可以将所有公共依赖提取到根目录并链接各个仓库。这样,可以确保所有仓库用的都是一个版本。<br />有种错误的链接方式是yarn workspace 一个lerna包 add 另一个lerna包@0.1.0,这种必须使用版本号安装,否则报错,如果用了lerna link convert,就不需要使用这种方式。<a name="FGZmr"></a>### 12、lerna create <name> [loc]新建包 name包名,loc 位置可选```bash# 根目录的package.json"workspaces": ["packages/*","packages/@gp0320/*"],# 创建一个包gpnote默认放在 workspaces[0]所指位置lerna create gpnote# 创建一个包gpnote指定放在 packages/@gp0320文件夹下,注意必须在workspaces先写入packages/@gp0320,看上面lerna create gpnote packages/@gp0320
13、lerna version
这个命令 识别出修改的包 —> 创建新的版本号 —> 修改package.json —> 提交修改 打上版本的tag —> 推送到git上。
—allow-branch 
设置git上的哪些分支允许执行 lerna version 命令, 也可以在lerna.json中设置
{"command": {"publish": {"allowBranch": "master"}}}多个{"command": {"publish": {"allowBranch": ["master", "feature/*"]}}}
具体其他参数:lerna的基础使用
14、lerna publish
lerna publish 就是将 monorepo 需要发布的包,发布到 npm registry 上面去。
会打tag,上传git,上传npm。
分为几种不同的场景去运行:
lerna publish 永远不会发布 package.json 中 private 设置为 true 的包
lerna publish# lerna version + lerna publish from-git# 发布自上次发布来有更新的包(这里的上次发布也是基于上次执行lerna publish 而言)lerna publish from-git# 发布当前 commit 中打上 annoted tag version 的包(即 lerna publish from-git)lerna publish from-packages# 发布 package 中 package.json 上的 version 在 registry(高于 latest version)不存在的包# 发布在最近 commit 中修改了 package.json 中的 version (且该 version 在 registry 中没有发布过)的包(即 lerna publish from-package)
- from-git 即根据 git commit 上的 annotaed tag 进行发包
 - from-package 即根据 lerna 下的 package 里面的 pkg.json 的 version 变动来发包
 - —canary 发测试版本的包
 - 剩下不带参数的情况就直接走一个 bump version(即执行 lerna version)
 
configureProperties -> initialize -> execute
在 lerna 中,几乎所有子命令源码的执行顺序都是按照这样一个结构在进行,lerna 本身作为一个 monorepo,主要是使用 core 核心中的执行机制来去分发命令给各个子项目去执行
另外:如果你的包名是带scope的例如:”name”: “@gp0320/gpwebpack”,
那需要在packages.json添加 
"publishConfig": {"access": "public"},
lerna publishlerna info current version 0.1.4#这句意思是查找从v0.1.4到现在改动过的包lerna info Looking for changed packages since v0.1.4? Select a new version (currently 0.1.4) Patch (0.1.5)Changes:- daybyday: 0.1.3 => 0.1.5 #只改动过一个...Successfully published:- daybyday@0.1.5lerna success published 1 package
发布过程 
查找变更逻辑 
从上面这个图可以看到其中会有一些坑:
坑1:分支3的情况,因为开发者自己打的一些标签会影响lerna查找变更,可能会造成一些变更的包没有发布
解决办法:
1. 尽量避免自己打Tag
2. 或者只在一个专门的分支上,例如master,专门运行lerna publish进行发布,这个分支不能自己打其他Tag
坑2:几条分支同时进行的情况,可能生成了相同的版本号,从而发生版本冲突
解决办法:
1. 分支开发者之间约定好各自版本号
2. 或者只在一个专门的分支上,例如master,专门运行lerna publish进行发布
坑3:运行lerna publish如果中途有包发布失败,再运行lerna publish的时候,因为Tag已经打上去了,所以不会再重新发布包到NPM
解决办法:
1. 运行lerna publish from-git,会把当前标签中涉及的NPM包再发布一次,PS:不会再更新package.json,只是执行npm publish
2. 运行lerna publish from-package,会把当前所有本地包中的package.json和远端NPM比对,如果是NPM上不存在的包版本,都执行一次npm publish
版本号的变更
分为independent模式和fix模式,上面第一张图其实是independent模式,就是每个包有自己的版本号,再附上一张fix模式的图,主要的差异部分用红色标出来了
其他问题
lerna3.x只支持npm,内部的可以用@ali/lerna,但是比较头疼的是如果packages里面既有要发外部的包,又有要发内部的包
目前的解法是将所有要发内部的包,package.json里面加上private:true,发布的步骤变成了:
1. 运行lerna version更新package.json和打Tag,这一步也会更新private:true的包
2. 运行lerna publish from-git,将刚刚更新的包发布到npm,这一步会忽略private:true的包
3. 写个自动化脚本,先把private:true的包改为private:false,代用tnpm publish发布,然后再把private改回来
DEMO
初始化项目
$ npm install lerna -g$ mkdir lerna-gp && cd $_$ npm lerna init # 用的默认的固定模式,vue babel等都是这个# Add packages$ cd packages$ mkdir daybyday gpnode gpwebpack...#分别进入三个目录初始化成包$ cd daybyday$ npm init -y$ cd ../gpnode$ npm init -y$ cd ../gpwebpack$ npm init -y
完成后的目录结构:
➜ lerna-gp git:(master) ✗ tree.├── lerna.json├── package.json└── packages├── daybyday│ └── package.json├── gpnode│ └── package.json└── gpwebpack└── package.json4 directories, 5 files
修改顶层 package.json & lerna.json 配置
# package.json 文件加入"private": true,"workspaces": ["packages/*"],# lerna.json 文件加入"useWorkspaces": true,"npmClient": "yarn",
这样使得nodemodule会装在根目录并让所有包链接根目录node_modules。
参考:
- 官网:https://github.com/lerna/lerna
 - 中文文档:https://github.com/minhuaF/blog/issues/2
 - 深入 lerna 发包机制 —— lerna publish:https://zhuanlan.zhihu.com/p/362042945
 - 使用lerna管理monorepo及发npm包实战教程:https://zhuanlan.zhihu.com/p/404166248
 
