lerna
关于 lerna
将大型代码库拆分为独立版本的包对于代码共享来说十分有用。然而,跨许多存储库(across many repositories)进行更改是混乱的,并且很难跟踪,而且跨存储库进行测试会变得非常复杂。
为了解决这些(以及许多其他)问题,一些项目将把它们的代码库组织到多包存储库中(有时称为monorepos)。像Babel、React、Angular、Ember、Meteor、Jest等项目都在一个存储库中开发它们的所有包。
Lerna 是一个工具,它优化了使用 git 和 npm 管理多包存储库的工作流
个人理解:一个项目就是一个存储库,存储库(也叫仓库)是 git 里面的概念。所以我们就是为了实现的就是在一个存储库里面管理多个包,每个包都有自己的版本和域(scope),方便 版本控制 与 发布。lerna 这个工具帮我们优化了这些工作流方便我们使用。
Lerna 的 repo 看起来应该是什么样子的?
实际上这没什么。你有一个像这样的文件结构:
my-lerna-repo/
package.json
packages/
package-1/
package.json
package-2/
package.json
Lerna 可以做什么?
Lerna 有两个主要的命令是 lerna bootstrap
和 lerna publish
。
bootstrap
将链接 repo 的依赖到一起,publish
将帮助发布任何更新的包。
Lerna 不能做什么?
Lerna不是针对无服务器 monorepos 的部署工具。提升可能与传统的无服务器 monorepo 部署技术不兼容。
开始
下面的指令是适用于 Lerna 3.x 的。我们建议在新项目中使用它代替 2.x。
让我们使用 npm 安装 Lerna 并将其作为开发依赖在你的项目里。
$ npm install lerna -g // npm 方式安装 lerna
$ yarn global add lerna // yarn 方式安装 lerna
$ mkdir lerna-repo && cd $_ // 创建目录并切换到该目录
$ npx lerna init // 使用 lerna 初始化该目录
这个将创建一个 lerna.json
配置文件和一个 packages
目录,所以你的目录看起来应该是这样子的:
lerna-repo/
packages/
package.json
lerna.json
// lerna.json
{
"packages": [
"packages/*"
],
"version": "0.0.0"
}
// package.json
{
"name": "root",
"private": true,
"devDependencies": {
"lerna": "^3.6.0"
}
}
lerna 是如何工作的
lerna 有两种模式让你去管理你的项目:固定(Fixed)或独立(Independent)。
Fixed/Locked 模式(默认)
固定模式。该模式为单版本号,在根目录中的lerna.json
中设置。当使用lerna publish
时,如果自从上次发布后有模块改动,那么将会更新到新发布的版本。
这也是目前Babel用的模式,当你想要自动整合不同包的版本时使用这个模式。它的特点是任何package的major change均会导致所有包都会进行major version的更新。
Independent 模式
lerna init --independent
// 独立模式的 lerna.json
{
"packages": [
"packages/*"
],
"version": "independent"
}
独立模式。该模式中允许开发者独立管理多个包的版本更新。每次发布时,会得到针对每个包改动(patch, minor, major custom change)的提示。lerna会配合git,检查文件变动,只发布有改动的package。
独立模式允许你更具体地更新每个包的版本,并且对于一组组件是有意义的。将这种模式和 semantic-release 结合起来就不会那么痛苦了。
独立模式允许开发者更新指定package的版本。将lerna.json中的version键设为independent来启用独立模式。
概念
Lerna 将日志记录到 lerna-debug.log
文件中(类似于 npm-debug.log) 当运行命令发生错误的时候。
Lerna 也支持 scoped packages
运行 lerna --help
查看更多可用的命令和选项。
lerna.json
{
"version": "1.1.3",
"npmClient": "npm",
"command": {
"publish": {
"ignoreChanges": ["ignored-file", "*.md"],
"message": "chore(release): publish"
},
"bootstrap": {
"ignore": "component-*",
"npmClientArgs": ["--no-package-lock"]
}
},
"packages": ["packages/*"]
}
-
version
: 当前仓库的版本。 -
npmClient
: 一个选项执行客户端使用哪种命令运行。npm
和yarn
-
packages
: 要用作包位置的全局变量数组。
lerna.json
中的 packages 配置的数组值是用来匹配包含 package.json
文件的目录,这也是 lerna 被认为是 ‘叶子’ 包。
默认情况下,lerna 初始化 packages 值为 ["packages/"],但是你也可以使用另一个目录例如["modules/"], 或 ["package1", "package2"]。这个数组值定义的值都是相对于 lerna.json
文件所在的目录,一般是存储库的根( the repository root)。唯一的限制是不能直接嵌套包位置,但这也是“普通” npm 包共享的限制。
例如,["packages/", "src/*"] 匹配这样的树:
packages/
├── foo-pkg
│ └── package.json
├── bar-pkg
│ └── package.json
├── baz-pkg
│ └── package.json
└── qux-pkg
└── package.json
src/
├── admin
│ ├── my-app
│ │ └── package.json
│ ├── stuff
│ │ └── package.json
│ └── things
│ └── package.json
├── profile
│ └── more-things
│ └── package.json
├── property
│ ├── more-stuff
│ │ └── package.json
│ └── other-things
│ └── package.json
└── upload
└── other-stuff
└── package.json
在packages/*下定位叶子包(每个包看起来像片叶子)被认为是一种“最佳实践”,但不是使用Lerna的必要条件。