前言

将大型代码仓库分割成多个独立版本化的软件包(package)对于代码共享来说非常有用。但是,如果某些更改 跨越了多个代码仓库的话将变得很 麻烦 并且难以跟踪,并且跨越多个代码仓库的测试将迅速变得非常复杂。
为了解决这些(以及许多其它)问题,某些项目会将 代码仓库分割成多个软件包(package),并将每个软件包存放到独立的代码仓库中。

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

入门

1
2
3
npm install --global lerna                    // 全局安装 lerna
git init hfs-lerna-test && cd hfs-lerna-test // 创建文件夹, hfs-lerna-test 仅代表当前例子文件夹名
lerna init // 初始化一个lerna项目结构,如果希望各个包使用单独版本号可以加 -i | --independent

你的代码仓库目前应该是如下结构:

1
2
3
4
- hfs-lerna-test/
- packages/
- package.json
- lerna.json

运作方式

Fixed 模式(默认)

固定模式,也就是我们初始化时默认采用的模式。该模式为单版本号,在根目录 lerna.json 中设置,该模式你可以理解为 ‘全量发布’,即任何一个模块更新了,当你在执行 lerna publish 发布时,所有的模块都会统一更新版本号。Babel 目前就是采用该模式。

Independent 模式

lerna init --independent

独立模式的 Lerna 项目允许维护者单独升级包版本,可以理解为’增量发布’。每次发布时,您都会收到有关已更改的每个包的提示,以指定它是补丁、次要、主要还是自定义更改。

这种方式相对第一种来说,更灵活,只需将只需将 lerna.json 中的 version 键改成 independent 即可启用 independent 模式。

创建模块

lerna create test-1
lerna create test-2

执行上面的命令后会在 packages 中创建对应的模块,并根据提示生成 package.json

hexo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── lerna.json
├── package.json
└── packages
└── test-1
├── __tests__
│ └── test-1.test.js
├── lib
│ └── test-1.js
├── package.json
└── README.md
└── test-2
├── __tests__
│ └── test-2.test.js
├── lib
│ └── test-2.js
├── package.json
└── README.md

依赖管理

  • 我们时常看到某些一类诸如 @babel/runtime@babel/preset-env的依赖包,其中 babel 实际就是可以看成包组织作用域的意思,代指分将babel包分割成多个独立版本化的软件包(package)。
  • npm 包前面加 @,代表 scopes 相关的包,可以理解为作用域(范围)包, npm 作用域的命名不是谁便就能用的,只有两种可以使用:自己的用户名、自己创建的组织名。
  • 因此在 test-1test-2 中,package.jsonname 字段分别改成 @hfs-lerna-test/test-1,@hfs-lerna-test/test-2

在仓库根目录执行指令安装依赖

1
lerna bootstrap

lerna bootstrap,会安装当前目录下所有定义在 package.json 中的依赖包。相当于给 packages 下的每个软件包执行 npm install

如果 test-2/package.json 中引用了test-1的依赖,执行后将自动将其添加到依赖包中。

1
2
3
"devDependencies": {
"@hfs-lerna-test/test-1": "0.0.1"
},

如下:

1
2
3
4
5
6
7
8
9
└── test-2
├── __tests__
│ └── test-2.test.js
├── node_modules
│ └── @hfs-lerna-test/test-1
├── lib
│ └── test-2.js
├── package.json
└── README.md

发布模块

lerna publish

  • 执行时会打 Tag,上传 Github ,上传 NPM
  • 当我们执行 lerna publish 命令时,可能会报错,lerna publish 常见错误见 常见错误类型
  • lerna publish 本质上还是执行 npm publish,那么我们首先需要在 npm 仓库上注册用户,以及推送代码到远程 github 仓库。

hexo

  • 注意

    • npm publish 默认发布私有包,想要发布公共包必须使用

      1
      npm publish --acsess=public
    • 复制代码也可以在 packages 下的每个软件包的 package.json 中配置

      1
      2
      3
      "publishConfig": {
      "access": "public"
      },

推动远程

  • 登录 Github
  • 创建代码仓库,如:git@github.com:<username>/hfs-lerna-test.git
  • 提交代码,推送到指定远程仓库
1
2
3
4
5
6
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:<username>/hfs-lerna-test.git
git push -u origin main

登录 npm 账户

1
2
3
4
5
6
7
# 查看是否登录
npm whoami

# 没有则登录
npm login

# 输入 username password

【发布】使用非组织包方式

使用非组织包方式,需要设置报名为当前 npm 账户名为前缀。

1
2
3
npm adduser
Username: @hfs-lerna-test // 自己的username
Password: // 密码

【发布】组织包 scope packages 方式(推荐)

上诉我们定义的 @hfs-lerna-test,即代表这里的 scope@hfs-lerna-test

  • 在 npm 仓库中创建团队,登录 NPM
    hexo

  • 将自己的账号添加到该团队组织中
    hexo
    hexo

  • 最后,可以开始愉快地 lerna publish

Lerna 更多命令

1
2
3
4
lerna init        // 初始化
lerna clean // 删除 node_modules
lerna bootstrap // 安装依赖
lerna list // 列出包名

lerna 的详细用法,请参考 lerna

lerna publish 常见错误类型

  • 未推送远程仓库

    解决:创建 Github 远程仓库,绑定并推送远程。

    1
    2
    3
    $ lerna publish
    lerna ERR! ENOREMOTEBRANCH Branch 'master' doesn't exist in remote 'origin'.
    lerna ERR! ENOREMOTEBRANCH If this is a new branch, please make sure you push it to the remote first.
  • 401 npm 未登录

    解决:执行 npm whoami 查看是否 NPM 已登录,未登录添加账户或登录。

    1
    2
    $ lerna publish
    lerna ERR! EWHOAMI Authentication error. Use `npm whoami` to troubleshoot.
  • 402 您必须注册私人包

    解决:当前发布的 npm 包是 @ 类型为私人包,但是 NPM 上不存在该包组织,或者该组织为公共包。
    请变更包类型,或者通过共有包发布,详情见 发布模块 - 注意

    1
    2
    3
    $ lerna publish
    lerna ERR! E402 You must sign up for private packages

  • Scope 包组织未找到

    当前发布的 npm 包是 @ 类型,核对 scope 是否正确,需用自己的用户名、或者自己创建的团队组织名。

    1
    lerna ERR! E404 Scope not found