lerna从入门到源码实现

1,452 阅读4分钟

1. lerna简介

概要

lerna是GitHub上面开源的一款js代码库管理软件, 用来对一系列相互耦合比较大、又相互独立的js git库进行管理。解决各个库之间修改混乱、难以跟踪的问题。lerna可以优化这种情形下的工作流。

对于一些功能比较全的库,我们往往会把各个小功能拆分成独立的npm库,他们直接有比较强的依赖关系。比如:Babel、React等开源代码都是按照这种方式进行处理的。

  • Lerna 官网 是一个管理工具,用于管理包含多个软件包(package)的 JavaScript 项目

  • Lerna Git 仓库 是一种工具,针对 使用 git 和 npm 管理多软件包代码仓库的工作流程进行优化

代码库结构

lerna管理的库文件结构类似如下这样


my-lerna-repo/

package.json

packages/

package-1/

package.json

package-2/

package.json

lerna主要做了什么

  • 通过lerna的命令lerna bootstrap 将会把代码库进行link。

  • 通过lerna publish发布最新改动的库

Get Started

首先使用 npm 将 Lerna 安装到全局环境中


npm install -g lerna

接下来,我们将创建一个新的 git 代码仓库


mkdir lerna-repo&&cd lerna-repo

现在,我们将上述仓库转变为一个 Lerna 仓库:


lerna init

默认会创建lerna.json和packages目录, 如下


lerna-repo/

packages/

package.json

lerna.json

lerna如何工作的

默认lerna有两种管理模式, 固定模式和独立模式

固定模式

固定模式,通过lerna.json的版本进行版本管理。当你执行lerna publish命令时, 如果距离上次发布只修改了一个模块,将会更新对应模块的版本到新的版本号,然后你可以只发布修改的库。

这种模式也是Babel使用的方式。如果你希望所有的版本一起变更, 可以更新minor版本号,这样会导致所有的模块都更新版本。

独立模式

独立模式,init的时候需要设置选项 --independent. 独立模式允许管理者对每个库单独改变版本号,每次发布的时候,你需要为每个改动的库指定版本号。这种情况下, lerna.json的版本号不会变化了, 默认为independent。

lerna.json解析


{

"version": "1.1.3",

"npmClient": "npm",

"command": {

"publish": {

"ignoreChanges": [

"ignored-file",

"*.md"

]

},

"bootstrap": {

"ignore": "component-*",

"npmClientArgs": ["--no-package-lock"]

}

},

"packages": ["packages/*"]

}

  • version , 当前库的版本

  • npmClient , 允许指定命令使用的client, 默认是 npm, 可以设置成 yarn

  • command.publish.ignoreChanges , 可以指定那些目录或者文件的变更不会被publish

  • command.bootstrap.ignore , 指定不受 bootstrap 命令影响的包

  • command.bootstrap.npmClientArgs , 指定默认传给 lerna bootstrap 命令的参数

  • command.bootstrap.scope , 指定那些包会受 lerna bootstrap 命令影响

  • packages , 指定包所在的目录

命令行

lerna publish

发布新的库版本


lerna publish // 发布最新commit的修改

lerna publish <commit-id> // 发布指定commit-id的代码

更新包


git add .

git commit -m "test:"

lerna updated

lerna publish

注意 lerna updated 命令需要提交更改后才会生效,再次 lerna publish

lerna run

执行package.json中的脚本命令


lerna run test # 运行所有包的 test 命令

lerna run --scope my-component test # 运行 my-component 模块下的 test

lerna run --parallel watch # 观看所有包并在更改时发报,流式处理前缀输出

lerna create

新建包

lerna add

添加一个包的版本为各个包的依赖


$ lerna add <package>[@version] [--dev] [--exact]

添加单独依赖


lerna add <package> --scope=包名

假设moduleA 自己依赖 jquery,moduleB 自己依赖 zepto


lerna add jquery --scope=@fengyinchao/modulea

lerna add zepto --scope=@fengyinchao/moduleb

注意 scope 的值对应的是 package.json 中的 name 字段

添加packages里其它模块作为自己的依赖

假设moduleA 依赖 moduleB


lerna add @lerna-repo/moduleb --scope=@lerna-repo/modulea

注意这种依赖不会添加到 moduleA 的 node_modules 里,但会添加到 moduleA 的 package.json 中,它会自动检测到 @fengyinchao/moduleb 隶属于当前项目,直接采用symlink的方式关联过去

lerna bootstrap

bootstrap作了如下工作

  • 为每个包安装依赖

  • 链接相互依赖的库到具体的目录

  • 执行 npm run prepublish

  • 执行 npm run prepare

抽离公共模块

上面 moduleA 和 moduleB 都依赖了 lodash,且在各自 package 下的node_modules 里都有副本,这其实很浪费空间,可以使用 --hoist


lerna bootstrap --hoist

这会将 packages 里重复的依赖提取到最外层的 node_modules 里,同时最外层的 package.json 也不会更新 dependency 信息,所以不建议将公用依赖写到最外层的package.json里,而是重复写到每个子package.json 里,然后用 --hoist 提取出来

更新公共依赖

假设要升级 moduleA 和 moduleB 都依赖的 lodash 版本,不必依次到各子package下升级,可以借助 lerna-update-wizard 这个包来做


// 根目录执行

npm install --save-dev lerna-update-wizard

./node_modules/.bin/lernaupdate

卸载包

给 moduleA 移除一个依赖 husky


lerna exec --scope=@fengyinchao/modulea npm uninstall husky

lerna link

链接互相引用的库

2.实现lerna 的 init create命令

仓库地址