pnpm 管理 monorepo

1,960 阅读2分钟

使用 pnpm workspace 管理 monorepo 项目

安装 pnpm

pnpm 中文文档

什么是 monorepo

Monorepo 是项目管理代码的方式之一,指的是一个大项目中包含了需要小项目或者模块,大概的样子

├── packages
|   ├── pkg1
|   |   ├── package.json
|   ├── pkg2
|   |   ├── package.json
├── package.json

初始化项目

pnpm init

初始化一个项目,创建 package.json

.
└── package.json

在根目录中创建 pnpm-workspace.yaml

packages:
  - 'packages/**'

在根目录中创建 packages 文件夹,并且在 packages 中创建子项目

mkdir packages
mkdir packages/core
mkdir packages/browser

.
├── package.json
└── packages
    ├── browser
    └── core

安装全局依赖

-D 表示安装到开发依赖,即 devDependencies, -w 表示在根目录安装

pnpm install typescript -Dw

.
├── node_modules
│   └── typescript -> .pnpm/typescript@4.7.4/node_modules/typescript
├── package.json
├── packages
│   ├── browser
│   └── core
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

局部依赖

首选在每个子项目中初始化 package.json

.
├── node_modules
│   └── typescript -> .pnpm/typescript@4.7.4/node_modules/typescript
├── package.json
├── packages
│   ├── browser
│   │   └── package.json
│   └── core
│       └── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

packages/browser 中的 package.json

{
  "name": "@test/browser",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

packages/core 中的 package.json

{
  "name": "@test/core",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

这里需要给每个子项目指定命名空间和包名,即 @test/core

通过 filter 可以给指定的包安装依赖

pnpm install loadsh -r --filter @test/core
{
  "name": "@test/core",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.21"
  }
}

两个包之间相互依赖

browser 依赖 core

pnpm i @test/core -r --filter @test/browser

browser 中就依赖上了 core,版本前面有 workspace 字样,表示本地引用,发布的时候就会被剔除掉

{
  "name": "@test/browser",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@test/core": "workspace:^1.0.0"
  }
}

运行子包命令

根目录 package.json

使用 pnpm -r --filter=@test/* run test 来批量执行子包中的 test 脚本

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "pnpm -r --filter=@test/* run test"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^4.7.4"
  }
}

@test/browser 中的 package.json

{
  "name": "@test/browser",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "test browser""
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@test/core": "workspace:^1.0.0"
  }
}

@test/core 中的 package.json

{
  "name": "@test/core",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "test core""
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.21"
  }
}

执行 pnpm test,即可得到输出结果

> pnpm -r --filter=@test/* run test

Scope: 2 of 3 workspace projects
packages/core test$ echo "test core"test core
└─ Done in 13ms
packages/browser test$ echo "test browser"test browser
└─ Done in 8ms