项目代码组织:Polyrepo vs. Monorepo
项目代码组织主要有两种方式:
许多大型科技公司都使用Monorepo,例如Yandex。Monorepo有助于在团队和业务部门之间共享创意和最佳实践。更多关于Monorepo和相关工具的信息,请访问https://www.php.cn/link/b01ccf4f29b57b0b1bdb9407050db28d。本文将重点介绍使用PNPM构建Monorepo的简单方案。
PNPM使用工作区(workspaces)来整合单个仓库中的多个项目。
环境搭建
首先,创建一个空文件夹作为新的Monorepo项目。在仓库内初始化PNPM:
<code class="language-bash">pnpm init</code>
然后,创建一个pnpm-workspace.yaml
文件,描述包文件夹:
<code class="language-yaml">// pnpm-workspace.yaml packages: - 'packages/**' - 'apps/**'</code>
/packages
文件夹存放共享库,/apps
文件夹存放应用程序(例如,独立的React Native移动应用程序和使用相同组件或连接库与API服务器通信的Web应用程序)。
本文将使用一个Telegram发布机器人为例,其源代码位于GitHub:https://www.php.cn/link/8164ca2fe04767628ac1c6813e8a0867。下载并解压到/apps/publish-bot
文件夹,然后运行安装命令:
<code class="language-bash">pnpm install</code>
创建Telegram工具包
在/packages
文件夹中创建一个名为telegram-utils
的文件夹,初始化PNPM和TypeScript:
<code class="language-bash">pnpm init && pnpm add -D typescript && pnpm tsc --init</code>
该包将提供一个函数,用于组合来自所有消息(文本、视频和照片)的文本和标题。需要安装Telegraf包:
<code class="language-bash">pnpm add telegraf</code>
所有源代码都应位于/src
目录中。为了方便功能分组,建议创建不同的文件夹。组合文本的功能位于/texts
文件夹中,代码如下:
<code class="language-typescript">// packages/telegram-utils/src/texts/combineTexts.ts import { Message } from 'telegraf/types'; import { FmtString, join } from 'telegraf/format'; type GroupedMessages = { photos: Array<Message.PhotoMessage>; videos: Array<Message.VideoMessage>; text: Array<Message.TextMessage>; }; export const combineTexts = ({ photos, videos, text }: GroupedMessages) => { const photoTexts = photos .map(photo => photo.caption ? new FmtString(photo.caption, photo.caption_entities) : undefined) .filter((t): t is Required<FmtString> => t !== undefined); const videoTexts = videos .map(video => video.caption ? new FmtString(video.caption, video.caption_entities) : undefined) .filter((t): t is Required<FmtString> => t !== undefined); const allTexts = []; if (text.length) allTexts.push(join(text.map(t => new FmtString(t.text, t.entities))), '\n'); if (photoTexts.length) allTexts.push(join(photoTexts, '\n')); if (videoTexts.length) allTexts.push(join(videoTexts, '\n')); return join(allTexts, '\n'); };</code>
代码说明:
undefined
;创建/texts
文件夹的索引文件:
<code class="language-bash">pnpm init</code>
在package.json
文件中使用exports
字段设置包功能的导出:
<code class="language-yaml">// pnpm-workspace.yaml packages: - 'packages/**' - 'apps/**'</code>
为了在应用程序中识别Monorepo包,为所有包添加前缀@monorepo
。重命名package.json
文件中的telegram-utils
包:
<code class="language-bash">pnpm install</code>
添加构建脚本:
<code class="language-bash">pnpm init && pnpm add -D typescript && pnpm tsc --init</code>
完整的package.json
文件:
<code class="language-bash">pnpm add telegraf</code>
配置TypeScript编译器:启用增量编译以节省构建时间,仅处理更改的部分;启用复合编译以使用项目引用。将/src
文件夹定义为rootDir
,将包的outDir
定义为/dist
。更新后的tsconfig.json
:
<code class="language-typescript">// packages/telegram-utils/src/texts/combineTexts.ts import { Message } from 'telegraf/types'; import { FmtString, join } from 'telegraf/format'; type GroupedMessages = { photos: Array<Message.PhotoMessage>; videos: Array<Message.VideoMessage>; text: Array<Message.TextMessage>; }; export const combineTexts = ({ photos, videos, text }: GroupedMessages) => { const photoTexts = photos .map(photo => photo.caption ? new FmtString(photo.caption, photo.caption_entities) : undefined) .filter((t): t is Required<FmtString> => t !== undefined); const videoTexts = videos .map(video => video.caption ? new FmtString(video.caption, video.caption_entities) : undefined) .filter((t): t is Required<FmtString> => t !== undefined); const allTexts = []; if (text.length) allTexts.push(join(text.map(t => new FmtString(t.text, t.entities))), '\n'); if (photoTexts.length) allTexts.push(join(photoTexts, '\n')); if (videoTexts.length) allTexts.push(join(videoTexts, '\n')); return join(allTexts, '\n'); };</code>
整合
回到/apps/publish-bot
,将@monorepo/telegram-utils
包添加到依赖项中。注意,不需要指定包版本,使用workspace:*
表示:
<code class="language-typescript">// packages/telegram-utils/src/texts/index.ts export * from './combineTexts';</code>
安装依赖项:
<code class="language-json">// packages/telegram-utils/package.json "exports": { "./texts": { "import": "./src/texts/index.ts", "require": "./dist/texts/index.js" } }</code>
更新发布机器人的preview
命令:
<code class="language-json">// packages/telegram-utils/package.json "name": "@monorepo/telegram-utils"</code>
更新/apps/publish-bot/tsconfig.json
:
<code class="language-json">// packages/telegram-utils/package.json "scripts": { "build": "tsc -p tsconfig.json" }</code>
构建应用代码前,需要先构建所有依赖项:
<code class="language-json">// packages/telegram-utils/package.json { "name": "@monorepo/telegram-utils", "version": "1.0.0", "main": "index.js", "scripts": { "build": "tsc -p tsconfig.json" }, "keywords": [], "license": "ISC", "exports": { "./texts": { "import": "./src/texts/index.ts", "require": "./dist/texts/index.js" } }, "devDependencies": { "typescript": "^5.7.3" }, "dependencies": { "telegraf": "^4.16.3" } }</code>
总结
现在,发布机器人使用内部共享库/包,并位于Monorepo中。这使得快速构建新功能和在多个应用程序中重用代码成为可能。
图片来自Unsplash的Gabriel Heinzer
以上是使用 PNPM 在 Monorepo 中构建并运行您的项目的详细内容。更多信息请关注PHP中文网其他相关文章!