Lerna + yarn 实现 monorepo 管理

5,986 阅读2分钟

.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}pre{font-size: 24px;line-height: 1.8}

介绍

Monorepo:是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。

Lerna:是一个管理多个 npm 模块的工具,是 Babel 自己用来维护自己的 Monorepo 并开源出的一个项目。优化维护多包的工作流,解决多个包互相依赖,且发布需要手动维护多个包的问题。

步骤

1. 全局安装 lernayarn

$ npm i -g lerna yarn

2. 初始化项目

$ mkdir ndogkit && cd ndogkit

$ lerna init

$ tree -a -C --dirsfirst -L 3 -I 'node_modules|.git'
.
├── packages
├── lerna.json
└── package.json

3. 添加 yarn workspace

(1) package.json

{
  ...
  "name": "root",
  /* 项目根目录下的private必须设置成true,否则workspace不会被启用 */
  "private": true,
  /* 指定需要管理的模块,这里的包组织方式参考的vuepress */
  "workspaces": [
    /* 所有模块包在此文件夹下 */
    "packages/@ndogkit/*",
    /* 文档说明 */
    "packages/doc",
    /* 脚手架相关 */
    "packages/ndogkit"
  ]
  ...
}

(2) lerna.json

{
  ...
  /* [doc](https://github.com/lerna/lerna/tree/master/commands) */
  "command": {
    "bootstrap": {
      /* 此模式:当多个包有相同依赖时会提升到顶层的node_modules,避免重复安装,减少体积 */
      "hoist": true,
      /* 在bootstrap期间,将参数传递给 npm install */
      "npmClientArgs": ["--no-package-lock", "--no-ci"]
    },
    "version": {
      "allowBranch": ["master"],
      /* 开启的话,提交当前所有更改,而不是添加新的提交,会跳过 `git commit`、`git push` */
      "amend": false,
      /* 自动生成changelog.md,详情请看[约定式提交](https://www.conventionalcommits.org/zh-hans/v1.0.0-beta.4/) */
      "conventionalCommits": true
    },
    "publish": {
      /* 跳过npm包访问权限 */
      "verifyAccess": false,
      /* 发布地址 */
      "registry": "http://npm.jsany.org"
    }
  },
  /* 使用yarn运行所有命令 */
  "npmClient": "yarn",
  /* 使用yarn的工作区(需在package.json中指定workspaces) */
  "useWorkspaces": true,
  /* 独立模式:该模式下只有改动过的 package 才会被发布,每次发布时,都会提示您已更改的每个软件包,以指定是补丁,次要,主要还是自定义更改,注意:使用约定式提交后,会根据commit生成对应版本,无需手动指定 */
  "version": "independent"
  ...
}

4. 添加模块

$ tree -a -C --dirsfirst -L 3 -I 'node_modules|.git|package-lock.json'
./packages
├── @ndogkit
│   ├── core
│   ├── egg-tools
│   └── shared-utils
├── docs
└── ndogkit
    ├── cmds
    ├── utils
    ├── cli.js
    ├── index.js
    └── package.json
# 查看所有package,注意 package.json 中若设置了"private": true,则不会展示
$ lerna ls
info cli using local version of lerna
lerna notice cli v3.19.0
lerna info versioning independent
@ndogkit/core
@ndogkit/egg-tools
@ndogkit/shared-utils
ndogkit
lerna success found 4 packages

注意:这里 lerna 取得模块名是来自 package.jsonname 属性

5. 安装相关依赖(四种)

(1) 安装所有依赖(bootstrap)

# --npm-client=yarn --hoist 会冲突
# 用 yarn workspace 特性替代 lerna bootstrap
$ yarn

(2)根项目安装依赖

# yarn 使用 workspace 模式安装 npm 包时必须加 -W 参数
$ yarn add -D -W [...pkg]

(3)给 package 安装外部依赖

$ yarn workspace @ndogkit/share-utils add chalk debug globby fs-extra mime

(4)给 package 安装内部依赖

# 一定要指定正确的版本号,不然会到npm查找包
$ yarn workspace @ndogkit/egg-tools add @ndogkit/shared-utils@1.0.0

6. 清除依赖

$ lerna clean && rm -rf ./node_modules

7. 运行命令

# lerna run [...cmd] --scope @scope
$ lerna run compile --scope @ndogkit/shared-utils

8. 发布

# 查看更改,同样 package.json 中设置了 "private": true 的 package 不会展示
$ lerna changed
# 每次发布会自动更新相关package的版本号,并且会更新引用该package的其他package依赖
$ lerna publish