0%

lerna入门

monorepo

monorepo(monolithic repository),与multirepo相对,分别是单代码仓库与多代码仓库(one-repository-per-module)
multirepo即传统做法,按模块分为多个代码库,实践中发现一些问题:

  1. issue管理混乱,经常有在core repo提module问题的,需要Close this and track that
  2. changelog难以整合,需要人工梳理所有变动的仓库,并做整合
  3. core repo版本更新麻烦,需要同步所有module更新其依赖的core repo版本

monorepo把所有相关module都放到一个repo里,每个module独立发布,但使用与该repo统一的版本号(例如Babel和React),issue和PR都集中到该repo,changelog可以简单地从一份commit列表梳理出来(甚至如果按照commit规范关联issue tag的话,能够自动生成规范的changelog)
monorepo也存在一些问题,但不如上面提到的痛点强烈:

  1. repo体积较大,可能带来版本控制的问题(Git不适合管理体积太大的repo)
  2. 统一构建工具,对构建工具提出了更高要求,要能构建各种相关module

从源码管理的角度来看,multirepo与monorepo是两种不同的理念,前者允许多元化发展,各个module可以有自己的玩法(构建,依赖管理,单元测试等),后者希望集中管理,减少玩法差异带来的沟通成本

monorepo标志性的特征是目录结构,例如React:

1
2
3
4
5
react-16.2.0/
packages/
react/
react-art/
react-.../

每个module都有自己的依赖项(package.json),能够作为独立的npm package发布,只是源码放在一起维护


创建lerna项目

1
2
3
4
5
// 安装
npm install lerna -g
git init hoho-lerna && cd hoho-lerna
// 初始化目录结构
lerna init

得到如下结构:

1
2
3
4
hoho-lerna/
packages/
lerna.json
package.json

创建一些模块

1
2
mkdir packages/core && cd packages/module-a
npm init

我们在module-a的package.json中加入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"name": "module_a",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
// 在此处加入对core的依赖
"core": "^1.0.0"
}
}

这时使用lerna bootstrap可以将每个库之间的依赖建立起来,你会发现module-a中的node_modules中有了core(引用的方式)


lerna常用命令

run

Run an npm script in each package that contains that script
在每个包含此命令的package中运行此命令

1
2
3
4
5
$ lerna run <script> -- [..args] # 在每个包含此命令的package中运行此命令
$ lerna run test
$ lerna run build
# 并行执行所有package watch命令
$ lerna run --parallel watch
配置
指定范围

lerna run 接收三个标志来控制执行包的范围--concurrency,--scope,ignore(更多请看这里)

  • --scope <glob>
    只在给定的package中执行

    1
    2
    3
    $ lerna exec --scope my-component -- ls -la
    $ lerna run --scope toolbar-* test
    $ lerna run --scope package-1 --scope *-2 lint

    对于某些glob需要使用option的参数来避免shell过早执行

  • --ignore <glob>

    1
    2
    3
    $ lerna exec --ignore package-{1,2,5}  -- ls -la
    $ lerna run --ignore package-1 test
    $ lerna run --ignore package-@(1|2) --ignore package-3 lint
  • --no-private
    排除私有package,默认情况下是包含他们的


--npm-client <client>

指定执行命令的包管理工具,默认是npm

1
$ lerna run build --npm-client=yarn

也可以在lerna.json中这样设置

1
2
3
4
5
6
7
{
"command": {
"run": {
"npmClient": "yarn"
}
}
}

stream

将子进程的输出以流的形式输出,以原始包名称为前缀,这会让不同包交错输出。

1
$ lerna run watch --stream

--parallel

与–stream类似,但完全忽略并发和拓扑排序,在带有前缀流输出的所有匹配包中立即运行给定的命令或脚本。这是长时间运行进程的首选标志,例如运行在许多软件包上的npm run watch

1
$ lerna run watch --parallel

建议在使用–parallel标志时约束此命令的范围,因为产生数十个子进程可能对shell的平等(或最大文件描述符限制)有害


--no-bail
1
lerna run --no-bail test

默认情况下lerna run遇到错误会退出,使用这个参数可以忽视错误


--no-prefix

输出为流时( - stream或–parallel),禁用包名称前缀。将结果传递给其他进程(如编辑器插件)时,此选项非常有用。


bootstrap

github home


参考:

  1. lerna入门指南
  2. lerna run