·  阅读

主流的 monorepo 做法是只有一个 git 仓库,然后建立一个 packages 目录,每个子目录都是一个 npm package,有自己的 package.json。

如果我们不用这个主流做法,而是每个 npm package 都有一个自己的 git 仓库,该如何维护 package.json 文件?

假设我们有 pkg-a,pkg-b,pkg-c 三个包,彼此有依赖关系。

列举可能遇到的问题:

  • 本地开发的时候如何让 pkg-a,pkg-b,pkg-c 依赖的都是本地正在开发的版本?
  • 本地开发能够选择 pkg-a,pkg-b,pkg-c 的指定版本进行集成开发测试?
  • 在 CI 服务器上跑测试的时候,pkg-a 如何找到 pkg-b,pkg-c,并使用正确的版本?
  • 如何频繁手工维护 package.json 升版本号?一旦忘记升版本,就导致一个包安装出两个版本来?
  • 拆分成多个 git 仓库之后,所有问题的实质都是单个 git 仓库的代码都是不完整的,必须组合了其依赖之后,才能跑单元测试

    每个包,都有两个形态

  • git 仓库形态:代码从 git 服务器 clone 下来
  • npm 包形态:代码从 npm registry 下载下来
  • yarn 具有 workspaces 的功能。可以写一个 project 级别的大 package.json,把一堆小的 workspaces 给整合起来,选择每个包是从 git 拉,还是从 npm registry 来拉。

    下面我们来看具体的解法。

    pkg-a 的 package.json 这么写

    "name": "pkg-a" , "version" : "0.1.0" , "peerDependencies" : { "pkg- b ": "*" , "pkg-c" : "*"

    这么写的道理是:

  • version 填 0.1.0 是你当前开发所基于的上一个版本的版本号。发布的时候产生新的 git commit,修改这个 version 字段,并打 git tag
  • peerDependencies 指定对 pkg-b,pkg-c 的依赖,表示 pkg-a 本身并不明确依赖一个固定的版本号,或者版本范围,这样就不需要频繁维护 package.json 文件来升级 dependencies 字段指定的版本号
  • 然后建立这样的本地开发目录结构

    project ->
        package.json
        packages ->
            pkg-a (git submodule)
            pkg-b (git submodule)
            pkg-c (git submodule)
    

    在 project/package.json 这么写

    "private": true, "name": "project", "workspaces": ["packages/*"]

    workspaces 是 yarn 的功能。当在 project 目录下执行 yarn install 的时候。会让 pkg-a,pkg-b,pkg-c 互相都依赖本地 workspaces 里指定的文件夹。

    packages 目录下的 pkg-a 和 pkg-b 这些都是 git submodule,需要独立 git commit 和 git push。

    如果不希望使用本地的 pkg-c,而是指定一个 npm registry 上的版本,可以不建立 packages/pkg-c 这个 submodule,然后修改一下 package.json

    "private": true, "name": "project", "workspaces": ["packages/*"], "dependencies": { "pkg-c": "0.1.0"

    这样我们就可以避免 submodule 越来越多的问题。同时也避免了单 git 仓库的 monorepo 越来越大的问题。可以自由选择把多少 pkg 用 git submodule 拉到本地来协同修改。

    那么在 CI 服务器上,pkg-a 如何跑自己的单元测试呢?只有 peerDependencies 无法 yarn install 自己的依赖啊。

    可以在 pkg-a 的目录下,新建一个 pkg-a/ci/package.json 文件

    "private": true, "workspaces": [".."], "scripts": { "postinstall": "cd .. && rm -rf node_modules && ln -s ci/node_modules" "dependencies": { "pkg-b": "git://github.com/xxx/pkg-b.git", "pkg-c": "git://github.com/xxx/pkg-c.git" "resolutions": { "pkg-b": "git://github.com/xxx/pkg-b.git", "pkg-c": "git://github.com/xxx/pkg-c.git"

    在 ci 脚本中,切换到 pkg-a/ci 子目录去做 yarn install,而不是在 pkg-a 自己这层目录做 yarn install

    name: Continuous Integration
    on: [push]
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
          - uses: actions/setup-node@v1
            with:
              node-version: 15.5.0
          - run: cd ci && yarn
          - run: yarn ci
    

    如果不加 resolutions 也可以,就是会报 warning。

    其中 postinstall 这个 script 是为了修复 pkg-a/node_modules 里的内容不正确的问题。一般 workspaces 是不会写 ".." 这样的路径的。属于 yarn 没有考虑过的使用方式。

  • 使用 peerDependencies 和 * 放宽对依赖的版本约束,免得天天改 package.json
  • 使用 yarn workspaces 链接本地的多个开发中的 package 目录
  • 给 CI 跑测试单独建一个 ci/package.json 指定 CI 时的依赖版本
  • 抄作业:github.com/rotcare/rx-…
  • 分类:
    前端
    标签: