前言
最近业余时间一直忙着开发 AudioDock:github.com/mmdctjj/Aud...
很少更新文章,但是今天抽空总结下最近开发时的一些思考,希望可以给大家带来新的开发思路!
pnpm + monorepo + AI = 效率翻倍
今天的主角是 pnpm ,不过还是得结合正在做的项目来说明!
我的项目是包含了桌面端、移动端的全栈项目,并且前后端分离,除此之外还包含了一些工具库。
整体使用 pnpm + monorepo 的形式管理项目。
bash
soundX/
├── apps/ # 应用层 (前端/客户端)
│ ├── mobile/ # 移动端应用 (React Native / Expo)
│ ├── desktop/ # 桌面端应用 (Electron)
│ └── mini/ # 迷你端/小程序应用
├── services/ # 后端服务层
│ └── api/ # 核心后端 API 服务 (通常是 NestJS)
├── packages/ # 公共模块与包 (内部依赖)
│ ├── db/ # 数据库模型与 Prisma 配置
│ ├── ws/ # WebSocket 通讯协议与逻辑
│ ├── utils/ # 公共工具函数
│ └── test/ # 测试辅助工具
├── Dockerfile # 容器化部署配置
├── docker-compose.yml # 多服务编排配置
├── package.json # 根目录配置与工作区管理
├── pnpm-workspace.yaml # pnpm 工作区定义
└── nginx.conf # Nginx 反向代理配置
最近一直重度使用 AI 开发,没想到这种结构,让我的开发更加流畅:
全量的上下文信息
AI 在开发时,始终是全量的上下文(前、后端)信息,让AI 生成一个请求函数和参数返回值类型,基本上可以很准确的实现
统一的类型管理
这是 pnpm + monorepo 最大的优点了,统一的类型管理和统一的构建范式,即使想让 AI 重构,很少会出现重构失败的情况
跨应用开发
我开发移动端的时候,如果对 AI 生成的效果不满意,经常让 AI 熟悉下桌面端的相同功能,在开发移动端的功能,效果会得到很大的提升!
遇到的问题
多模块复用
我们知道,一个标准的前后端分离项目,需要多个模块的配合:models、servies、views。
像我的这个项目移动端(React Native)和桌面端(Electron)都会出现这些模块,包括后续会实现的小程序和电视端,大概率还是需要这些的,所以,我将公共的部分抽离成了单独的包:db、services(后知后觉,还没抽离)!
让人摸不着头脑的 nestjs
我之前已经写过 nestjs 的文章了,介绍了他基础的开发范式,controller、servide、module。可以说是 spring boot 无痛切换 Node 的最佳方案了,但是在我第一次打包的时候傻眼了,打包之后一直报错:
javascript
Error: Cannot find module '@nestjs/core'
Require stack:
| - /usr/src/app/dist/main.js
一开始以为是我的 Dockerfile 配置的有问题,反复调整,折磨了我一个周末才发现这是在 pnpm monorepo 项目里开发必然会出现的问题。
解决方法很简单,构建的时候,在单独下载一次生产环境的依赖包就可以了!
sql
# 2. 安装生产依赖 + 全局安装 prisma (用于 db push)
RUN apt-get update -y && apt-get install -y openssl
RUN npm i -g pnpm prisma@6.6.0 && pnpm install --prod --frozen-lockfile --ignore-scripts
出现这个问题的主要原因是 pnpm 的软连接机制在打包的时候会失效,所以找不到这个包。
Prisma 让我又爱又恨
解决完第一个问题,以为可以正常运行我的包了,结果没想到这时候是 Prisma 的问题了。
Prisma 最大的好处是可以自动生成类型文件、基础的业务查询!
但是真的没想到,这些便捷的后面隐藏着无数的坑,第二个问题是 Prisma@6.x 版本默认输出客户端文件在 pnpm 项目里也会找不到文件在哪。
ruby
node:internal/modules/cjs/loader:1386
throw err;
^
Error: Cannot find module '.prisma/client/default'
Require stack: - /app/node_modules/.pnpm/@prisma+client@6.8.2_prisma@6.8.2_typescript@5.9.3__typescript@5.9.3/node_modules/@prisma/client/default.js
后来看到需要使用自定义输出路径才不会出现这个问题。
但是使用自定义文件输出的文件是 CMD 格式的文件,于是我将 Prisma 的版本升级到了最新的 7.x 版本,新版本要求必须使用自定义路径输出客户端文件,并且生成的是 ES 格式的文件,可以在前端直接引入类型。
但是玩玩没想到,输出之后的文件会有三个变量一直报错
bash
apps/api dev: generated/prisma/internal/prismaNamespace.ts:114:14 - error TS2742: The inferred type of 'DbNull' cannot be named without a reference to '.pnpm/@prisma+client-runtime-utils@7.0.0/node_modules/@prisma/client-runtime-utils'. This is likely not portable. A type annotation is necessary.
apps/api dev: 114 export const DbNull = runtime.DbNull
apps/api dev: ~~~~~~
apps/api dev: generated/prisma/internal/prismaNamespace.ts:121:14 - error TS2742: The inferred type of 'JsonNull' cannot be named without a reference to '.pnpm/@prisma+client-runtime-utils@7.0.0/node_modules/@prisma/client-runtime-utils'. This is likely not portable. A type annotation is necessary.
apps/api dev: 121 export const JsonNull = runtime.JsonNull
apps/api dev: ~~~~~~~~
apps/api dev: generated/prisma/internal/prismaNamespace.ts:128:14 - error TS2742: The inferred type of 'AnyNull' cannot be named without a reference to '.pnpm/@prisma+client-runtime-utils@7.0.0/node_modules/@prisma/client-runtime-utils'. This is likely not portable. A type annotation is necessary.
apps/api dev: 128 export const AnyNull = runtime.AnyNull
apps/api dev: ~~~~~~~
没办法又回退到了 6.x 版本直接手写 modal 了。
最后
目前已经开发完了大部分功能了,大概集中开发了两三个周末的时间,整体来说 pnpm + monorepo 协同 AI 开发我感觉是个人或者小团队开发的最快形式了。
可能会有小伙伴担心是不是很费大模型的 token ?
我没试过小项目,但是如果真的免费 token 用完了,可以再注册一个新账号继续使用。