从0开始使用pnpm构建一个Monorepo方式管理的demo

本文介绍Monorepo的概念及优势,并通过pnpm构建一个简单Monorepo项目实例,演示如何安装依赖及实现包间引用。

写在前面

Monorepo这个词你应该不止一次听说了,像Vue3、Vite、ElementPlus等优秀开源项目都是使用Monorepo的方式管理项目,且这里说到的这几个项目都是采用pnpm作为包管理工具。

这篇文章就使用pnpm构建一个简单的Monorepo方式管理的项目。

🍊 什么是Monorepo?什么是pnpm?

Q:什么是Monorepo?

A:Monorepo是一种项目管理方式,就是把多个项目放在一个仓库里面,可以参考神三元大佬的一篇文章:现代前端工程为什么越来越离不开 Monorepo?,这篇文章中介绍了Monorepo的概念、收益以及MulitRepo的弊端。

Q:什么是pnpm?

A:pnpm就是一个包管理工具,原生支持Monorepo,比npm和yarn更快一些,其他的可以参考官网和神三元大佬的另一篇文章:为什么现在我更推荐 pnpm 而不是 npm/yarn?

🍌 搞一个Monorepo的demo玩玩

现在我们就开始使用pnpm来构建一个Monorepo,在正事开始之前,你先需要保证你的电脑中具有Node.js,我的版本的是16.9.0

首先你需要有pnpm这个工具,安装的话可以从官网找方法,或者直接使用npm安装,命令如下:

npm i pnpm -g

现在我们开始搞事情。

第一步,创建一个项目的根目录,这里就叫monorepo-demo,咋创建都可,这里使用的是命令:

mkdir monorepo-demo

第二步,初始化package.json,这个没啥说的,命令如下:

pnpm init

这里我对内容做了一点修改,package.json的内容如下:

{
  "name": "monorepo-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "type": "module",
  "keywords": [],
  "author": "ywanzhou",
  "license": "MIT"
}

这里我主要添加了一个type字段,这里我使用ESModule模块化规范。

第三步,创建pnpm-workspace.yaml文件,这个文件定义了工作空间的根目录,内容如下:

packages:
  - 'packages/ **'

现在我们就可以在packages中创建多个项目了,目录结构如下:

monorepo-demo
├── package.json
├── packages
│   ├── components
│   │   ├── index.js
│   │   └── package.json
│   ├── core
│   │   ├── index.js
│   │   └── package.json
│   ├── utils
│   │   ├── index.js
│   │   └── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

第四步,编写每个项目的package.json,其实主要是编写一下名称,方便以后使用,这里我的如下:

{
  "name": "@packages/components",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "type": "module",
  "license": "ISC",
  "dependencies": {
    "lodash": "^4.17.21",
    "@packages/utils": "workspace:^1.0.0"
  }
}

剩余的两个名称分别为@packages/core@packages/utils

其实到这基本工作就准备好了,现在介绍一下如何安装依赖,如何在packageA中引用packageB。

🍋 安装依赖

就这个demo来说,我们如果在根目录下安装依赖的话,这个依赖可以在所有的packages中使用,如果我们需要为具体的一个package安装依赖怎么办?

cd到package的所在目录嘛?漏,大漏特漏,我们可以通过下面这个命令:

pnpm --filter <package_selector> <command>

例如我们需要在@packages/components安装lodash,命令如下:

pnpm -F @packages/components add lodash

-F等价于--filter

现在我们在往@packages/utils中安装一个dayjs,命令如下:

pnpm --filter @packages/utils add dayjs

🍌 packageA中引用packageB

现在我们就来实现package间的相互引用,首先我们在@packages/utils/index.js中写入如下内容:

import dayjs from 'dayjs'
export function format(time, f = 'YYYY-MM-DD') {
  return dayjs(time).format(f)
}

然后我们执行如下命令:

pnpm -F @packages/components add @packages/utils@*

这个命令表示在@packages/components安装@packages/utils,其中的@*表示默认同步最新版本,省去每次都要同步最新版本的问题。

安装完成后@packages/components/package.json内容如下:

{
  "name": "@packages/components",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "type": "module",
  "license": "ISC",
  "dependencies": {
    "@packages/utils": "workspace: *",
    "lodash": "^4.17.21"
  }
}

然后我们在@packages/components/index.js写入如下内容:

import { format } from '@packages/utils'
console.log(format(new Date()))

然后我们在项目根目录运行如下命令

node packages/components

即可打印出当前的日期。

<think> 嗯,用户询问在使用pnpm workspace管理monorepo仓库中如何实施commit lint的最佳实践。根据引用内容,用户提供的三个引用都涉及monorepopnpm workspace以及代码规范工具,但具体到commit lint的内容只有引用3提到了husky和lint-staged,这是commit lint的常见辅助工具。 用户可能正在搭建一个企业级monorepo项目,需要确保提交信息的规范性。深层需求可能包括:统一团队协作规范、自动化校验机制、与现有pnpm+changesets工作流的整合。考虑到monorepo的特殊性,单一仓库的commit校验需要支持多包作用域(scope)的灵活性。 从技术实现角度,最佳实践应包含四个层级:1)基础工具链(commitlint+husky);2)作用域动态识别(需pnpm特性支持);3)变更集联动(因引用提到changesets);4)IDE友好提示。特别要注意pnpm workspace下包关系的解析方式,比如通过`pnpm list -r --depth=-1 --json`可动态生成作用域列表。 用户未提及具体包管理器版本,需默认支持pnpm 7+的workspace协议。另外引用3中提到的biome虽是新锐lint工具,但commitlint生态仍以@commitlint为主流方案,建议保持工具一致性。 补充说明:用户提供的引用2提到monorepo的依赖管理优势,这在验证commit作用域时尤为关键——需要确保提交信息中的包名实际存在。因此方案中需加入workspace包名的实时校验逻辑。 </think> 在 pnpm workspace 管理monorepo 仓库中,实现 `commit lint` 的最佳实践需结合以下核心方案: --- ### 最佳实践方案 1. **基础工具链安装** ```bash pnpm add -Dw @commitlint/cli @commitlint/config-conventional husky ``` 2. **配置 commitlint 规则** 创建 `commitlint.config.js`: ```javascript module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'scope-enum': [2, 'always', getWorkspaceScopes()] // 动态校验包作用域 } }; // 自动获取所有 workspace 包名 function getWorkspaceScopes() { return require('fs') .readdirSync('packages', { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); } ``` 3. **Husky 钩子配置** ```bash # 启用 Git hooks pnpm exec husky install # 添加 commit-msg 钩子 pnpm exec husky add .husky/commit-msg 'pnpm exec commitlint --edit "$1"' ``` 4. **作用域动态验证(关键优化)** 在 `commitlint.config.js` 中实现动态作用域检测,确保: - 提交信息中的 `scope` 必须是 `packages/` 下的子包名 - 支持全局作用域(如 `docs`、`ci`) ```javascript function getWorkspaceScopes() { const scopes = new Set(['release', 'docs', 'ci']); const pkgs = require('fs').readdirSync('packages'); pkgs.forEach(pkg => scopes.add(pkg)); return Array.from(scopes); } ``` 5. **提交格式规范示例** 符合 Conventional Commits 的合法提交: ```plaintext feat(ui-button): 新增圆形按钮组件 fix(core-utils): 修复日期格式化时区错误 (close #123) chore(release): 发布v1.2.0版本 ``` --- ### 方案优势 1. **作用域自动化** 动态检测 `packages/*` 目录结构,无需手动维护作用域列表[^3] 2. **Monorepo 友好** - 支持为特定包提交(`feat(package-a): ...`) - 支持仓库级提交(`chore(release): ...`) 3. **变更集联动** 配合 `changesets` 时,自动识别作用域生成 changelog[^1][^2] 4. **严格校验** 阻止不符合规范的提交: ```plaintext × 错误提交: "update something" √ 合法提交: "fix(api): 修复登录接口500错误" ``` --- ### 常见问题处理 **Q:如何允许非作用域提交?** A:在 `rule` 中添加 `'scope-empty': [2, 'never']` 改为 `[2, 'always']` **Q:与 ESLint/Prettier 集成冲突?** A:在 `.husky` 目录分开钩子: ```bash # .husky/pre-commit (代码格式化) pnpm exec lint-staged # .husky/commit-msg (提交信息校验) pnpm exec commitlint --edit $1 ``` > 最佳实践参考:Angular 团队提出的 [Conventional Commits 规范](https://www.conventionalcommits.org/) 已被 Vue、React 等主流仓库采用[^3]。在 monorepo 中强制规范提交信息,可大幅提升协作效率和版本管理可靠性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值