我对 monorepo 的一些思考
我对 monorepo 的一些思考
- 我对 monorepo 的一些思考
- 前言
- 它的由来
- 技术选型
- 管理工具
- 语言与打包
- 调试工具
- 测试框架
- 代码规范与质量控制
- 本地引用与发包替换
- 发包流程
- Github 相关配置
- 部署
- 使用手册
- 功能特性总结
- 如何使用?
- 清除默认的包(可选)
- 模板包介绍
- packages
- apps
- 更新包的依赖
- 配置自动发包
- 1. 在 Github 进行 PR 和发包
- 2. 在 npm 发包
- 内置脚本
- 配置同步方式
- 参数
- 总结
前言
最近在自己的新项目中,使用了 monorepo-template 模板,并不断地做了不少改进。
在此,我想借此机会分享一下这个模板的演变过程,以及我对 monorepo
的一些思考。
它的由来
为了应对越来越多的发包场景,我创建了 npm-lib-template 模板
这是一个 git
单仓单 npm
包模板,使用 rollup
进行打包,然后再发布到 npm
和 github
然而,在后续开发中,我发现单仓库的模式在某些场景下难以应对。
例如,当需要引用其他自己的包,进行单元测试并再次发包时,这时候往往要在多个项目之间进行来回的切换更改,管理复杂度会迅速上升。
git submodule
也是另外一条路子,但是我对每次都要同步 hash
感到深恶痛绝,遂放弃 (不过某些极其特殊场景的实现,还是只能利用这个功能)。
因此,我决定要创建一个 monorepo 项目模板 ,以应对这些需求。
技术选型
管理工具
在 monorepo
的管理上,我选择了 pnpm
和 turborepo
这对组合,原因很简单:它们都非常快。
pnpm
又可以节省磁盘空间,又能够链接自特定的内容寻址存储库,是为快也turborepo
有构建缓存,是为快也too
.
语言与打包
我选择使用纯 TypeScript
来编写所有类库项目,并使用 tsup
/ unbuild
进行打包,默认输出格式为 cjs
和 esm
,并利用 package.json
中的 exports
字段进行分发。
调试工具
在直接调试时,我抛弃了 dist
+ sourcemap
的调试方式,使用了 tsx
,它非常适合调试 TypeScript
编写的 CLI
项目。
测试框架
测试方面,我选择了 vitest
。
它不仅速度快,还很好地支持 cjs
、esm
和 TypeScript
,同时也适合 monorepo
项目。你可以利用 turbo
来执行单个的 vitest
任务,也可以利用 vitest.workspace
来进行多任务测试。
最初我使用的是 jest
+ ts-jest
,但它对多格式的混合模块的支持不够理想,最终我选择了 vitest
。
代码规范与质量控制
为了保持代码质量,我使用了 eslint
和 stylelint
,并基于自己的配置包 @icebreakers/eslint-config
和 @icebreakers/stylelint-config
来进行代码格式化和规范化。
我还为 .vscode
配置了一些推荐插件和编辑器选项。
此外,通过 husky
添加了 git hook
,配合 lint-staged
对提交的代码进行校验。与 commitlint
结合使用,以确保 git
提交信息符合规范。
本地引用与发包替换
利用 publishConfig
会在发包的时候替换 package.json
字段的方式,在本地包相互引用的时候,都使用 Typescript
源文件的方式导出,在 publishConfig
里定义的导出为,真正在不同环境中,指向 dist
中不同格式的产物地址。
通过这种方式,可以大大加速整个 monorepo
的开发测试速度,避免反复通过 watch
来构建 dist
和 sourcemap
,也避免一构建出产物,对应的 Typescript
文件,就报错的问题。
发包流程
我采用了 changesets
,它在 monorepo
环境下发布非常方便。功能非常的多,具体可以查看官方文档。
Github 相关配置
在 .github
目录下,我提供了默认的 CI/CD
流程配置,以及用户提交 issue
时的模板。经过少量配置后,就可以实现自动发布 npm
包、创建 git tag
以及生成 GitHub release
。
此外,模板里还有许多为 GitHub
显示优化的 md
文档。
部署
文档网站通过 netlify.toml
配置部署在 Netlify
上。最初我使用的是 Vercel
,但由于国内访问速度的原因,最终迁移到了 Netlify
。
使用手册
功能特性总结
- 强大的
monorepo
管理 (pnpm
+turborepo
) - 单元测试 (
vitest
) - 包括
cli bin
全部都是typescript
- 代码规范与质量 (
eslint
+@icebreakers/eslint-config
+@icebreakers/stylelint-config
) git
提交规范 (husky
+commitlint
+lint-staged
)pnpm
部署Docker
模板Github Action
自动发布npm
,github release
包 (changeset
)- 配置文件同步升级
npx @icebreakers/monorepo@latest
如何使用?
首先,访问本模板的 Github 地址,然后按照一下条件:
-
有
Github
账号的,可以登录后,点击右上角的Use this template
按钮 -
没有
Github
账号的,可以点击Code
按钮,把这个仓库的源码,或clone
或下载到本地
然后在根目录 (pnpm-workspace.yaml
所在的位置) 执行 pnpm i
去安装依赖
没有
pnpm
的,可以使用npm i -g pnpm
来进行安装。什么! 你不会连
nodejs
还没安装吧?
清除默认的包(可选)
执行 pnpm script:clean
命令,可以删去大部分的初始 repo
,只保留一个 @icebreakers/bar
项目作为发包打包模板。
执行完成之后再去执行 pnpm i
来更新 pnpm-lock.yaml
, 并提交来锁定版本
模板包介绍
默认把 repo
放在 packages
和 apps
这 2
个目录里面
packages
@icebreakers/bar
-tsup
打包的库模板@icebreakers/foo
-unbuild
打包的库模板(不推荐,unbuild
很久没有更新了)@icebreakers/monorepo
- 本仓库的更新配置服务,可直接使用npx @icebreakers/monorepo
执行远端cli
命令
其中 tsup
是使用 esbuild
打包库的,unbuild
是使用老版本的 rollup
进行打包的
本来笔者是使用 rollup
来进行打包的 (weapp-tailwindcss
就是 rollup
打出来的) ,但是不够傻瓜无脑,所以用了 tsup
apps
@icebreakers/cli
- 使用typescript
编写的cli
模板@icebreakers/website
- 文档网站模板,也是 monorepo.icebreaker.top 的源代码
更新包的依赖
在根目录中执行 pnpm up -rLi
来进行包的交互式更新,下面是解释:
-r
: recursive 递归选中所有repo
-L
: latest 更新到最新-i
: interactive 交互式
配置自动发包
本项目使用 changesets 进行包的发布和 changelog
的生成
在使用的时候,首先你需要做一些配置:
-
首先你需要安装
Github App
: changeset-bot -
然后,来到你复制这个模板仓库(
repo
), 上方里的Settings
Tab 页面,进行 2 个操作:
1. 在 Github 进行 PR 和发包
选择 Code and automation
> Actions
> General
然后在右侧 Workflow permissions
下方选择: Read and write permissions
然后选中 Allow GitHub Actions to create and approve pull requests
然后保存即可。
这样 changeset
就有权限对你进行 PR
和代码版本更新了!
2. 在 npm 发包
选择 Security
> Secrets and variables
> Actions
然后在右侧的 Repository secrets
设置你的 NPM_TOKEN
这个可以在你的 npmjs.com
账号中生成获取
(假如你需要单元测试代码覆盖率,你需要设置 CODECOV_TOKEN
)
内置脚本
pnpm script:clean
删去大部分的初始repo
,只保留一个@icebreakers/bar
项目作为发包打包模板pnpm script:init
初始化一些package.json
里的字段pnpm script:sync
使用cnpm sync
功能,把本地所有的包,同步到npmmirror
上,需要安装cnpm
配置同步方式
在根目录下执行: npx @icebreakers/monorepo@latest
这个命令会把所有的文件从最新版本,对你本地进行覆盖,你可以从 git
暂存区把你不想要的文件剔除
参数
npx @icebreakers/monorepo@latest --raw
这个命令会从全部文件中去除 Github
相关的文件
npx @icebreakers/monorepo@latest -i
这个命令会进行命令行选择模式,你可以在这里对想要复制的文件进行筛选
当然你可以同时使用这 2
个命令
npx @icebreakers/monorepo@latest -i --raw
总结
从单仓到 monorepo
的转变不仅仅是对工具的选择,更是对项目管理模式的优化。
通过采用合适的工具链,能够更高效地管理多包项目的同时,利用 CI/CD
确保代码的质量和发布的顺畅。
希望我的这些思考对你有所帮助,也欢迎大家提出各种建议和意见。
相关文档见: monorepo.icebreaker.top 反正我以后创建项目的模板就用它了。
假如你都看到这了,说明我们是有缘之人,给我 monorepo-template 点个 Star
呗嘿嘿。
相关文章:

我对 monorepo 的一些思考
我对 monorepo 的一些思考 我对 monorepo 的一些思考 前言它的由来技术选型 管理工具语言与打包调试工具测试框架代码规范与质量控制本地引用与发包替换发包流程Github 相关配置部署 使用手册 功能特性总结如何使用?清除默认的包(可选)模板包介绍 packagesapps 更新…...

Java学习Day41:骑龙救!(springMVC)
springMVC与sevlet都是对应表现层web的,但是越复杂的项目使用SpringMVC越方便 基于Java实现MVC模型的轻量级web框架 目标: 小案例: 1.导入依赖 spring-context: 提供 Spring 框架的核心功能,如依赖注入、事件发布和其他应用上…...

Redis 常用命令总结
文章目录 目录 文章目录 1 . 前置内容 1.1 基本全局命令 KEYS EXISTS 编辑 DEL EXPIRE TTL TYPE 1.2 数据结构和内部编码 2. String类型 SET GET MGET MSET SETNX INCR INCRBY DECR DECYBY INCRBYFLOAT 命令小结 内部编码 3 . Hash 哈希类型 HSET …...
Mysql SqlServer 分页
一、MySQL分页 SELECT column1, column2 FROM table ORDER BY column1 LIMIT Offset, Fetch; SELECT column1, column2 FROM table WHERE id BETWEEN StartId AND EndId ORDER BY column1; 二、Sql Server 分页 SELECT column1, column2 FROM table ORDER BY column1 OFFSE…...

电子支付原理
电子支付原理 1.电子支付概述2.线下支付概念和一般流程线下支付技术分类 3.线上支付概念和一般流程 参考自:https://www.topsec.com.cn/uploads/2023-10-08/49dab9d0-004b-4955-808a-d1c83998b8191696745486491.pdf 1.电子支付概述 电子支付通用支付流程一般涉及四…...

什么是OAuth 2.0?OAuth 2.0的工作流程是什么?与OAuth 1.0有哪些区别?
在浏览网页时,你肯定会遇到允许你使用社交媒体账户登录的网站。此功能一般是使用流行的OAuth 2.0框架构建的。OAuth 2.0是对OAuth 1.0的彻底重写,OAuth 2.0与OAuth 1.0或1.1不向后兼容。 1. OAuth产生背景 为了更好的理解OAuth,我们假设有如…...

Unity+LeapMotion2的使用
开始吧 导入步骤1.到官网下载软件并安装2.安装插件3.场景中添加检测管理器4.场景中添加手部模型 更多细节 导入步骤 1.到官网下载软件并安装 地址 重启电脑后连接设备 可以看到连接成功 2.安装插件 (也可以看官方教程) Project—>PackageManag…...

【CanMV K230 AI视觉】 跌倒检测
【CanMV K230 AI视觉】 跌倒检测 跌倒检测 动态测试效果可以去下面网站自己看。 B站视频链接:已做成合集 抖音链接:已做成合集 跌倒检测 跌倒检测主要根据人体姿态来判断,可以用于老人、小孩跌倒监护。 实验名称:跌倒检测 实验…...

谈谈PCIe VID、DID、SSID、SSVID背后的智慧
PCIe Vendor ID 想了半天还是觉得从“ID是什么”这个问题开始比较好。那么ID是什么?ID就是身份。那身份又是什么?身份就是一个合理存在,用于区分不同个体。为什么叫“合理存在”呢?如果国家不给你发身份证,你就是黑户…...

9月11日
使用绘制事件完成钟表的绘制 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpSocket> #include<QMessageBox>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpub…...
昇腾310内存拷贝测试
目的 从服务器将数据拷贝到昇腾310 ai卡 结论 数据进入到服务器后的内存不能直接用昇腾acl接口拷贝到AI卡。 需要 1)先用acl接口申请内存; 2) 将数据拷贝到acl申请的内存 3)用acl接口将数据拷贝到AI卡 makefile # Copyri…...
‘$store‘ is not defined.
vueX不知道是否发挥作用?this.$store获取不到store/index.js中的数据 原因:版本问题 vuex版本不对,获取不到store 默认版本说明vuevue3vueXvue4vuex4只能在vue3中使用 vue默认vue3版本,vuex默认vuex4版本,vuex4只能…...
如何利用Linux提升工作效率和安全性?
Linux,作为一款自由和开放源代码的操作系统,已经在全球范围内得到了广泛的应用。无论是服务器、云计算、物联网,还是超级计算机,Linux都扮演着重要的角色。本文将深入探讨Linux的应用场景,以及如何利用Linux提升工作效…...

初始Linux 和 各种常见指令
目录 Linux背景 1. 发展史 Linux发展历史 1.历史 2. 开源 Linux下基本指令 01. ls 指令 02. pwd命令 03. cd 指令 04. touch指令 05.mkdir指令(重要): 06.rmdir指令 && rm 指令(重要): …...

【稀疏矩阵】使用torch.sparse模块
文章目录 稀疏矩阵的格式coocsrcsc Construction of Sparse COO tensorsConstruction of CSR tensorsLinear Algebra operations(稀疏与稠密之间混合运算)Tensor methods and sparse(与稀疏有关的tensor成员函数)coo张量可用的ten…...

如何增加谷歌网站曝光率?
增加谷歌网站曝光率其实就是让更多的人在搜索相关内容时,能看到你的网站。首先你就要搞清楚用户在搜索什么,这样才能把正确的内容呈现在他们面前。首先,你得站在用户的角度思考,想想他们在搜索与你网站相关的信息时,可…...
虚幻中的c++(持续更新)
文章目录 虚幻中的cUPROPERTY参数 UFUNCTION参数 虚幻中的c UPROPERTY 是虚幻中用于声明属性的宏,它用于标记某个属性是一个虚幻托管的属性,并且可以在编辑器中进行访问和操作。其提供了一系列参数,用于定义属性的各种行为,例如是…...
83-MySQL 索引有几种
MySQL中的索引主要有以下几种: 普通索引:最基本的索引类型,没有唯一性的限制,可以通过多个字段创建复合索引。 唯一索引:与普通索引类似,但区别在于唯一索引的每一个索引值只对应唯一的数据记录。 主键索…...

文献解读-The trans-omics landscape of COVID-19
关键词:流行病学;基因测序;变异检测; 文献简介 标题(英文):The trans-omics landscape of COVID-19 标题(中文):COVID-19的跨组学全景 发表期刊:…...

Unity核心实践小项目
要源码包的私信我。 简介 衔接Unity核心学习后的实操小项目 需求分析 准备工作 面板基类 为了能够控制一画布整体的透明度,所以需要给每个面板都添加一个 CanvasGroup组件 UI管理器 UGUI方面的参数设置 开始场景 场景搭建 直接用资源包搭建好的场景:…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...