前言
最近想把之前练习过的pnpm+turbo组合的monorepo项目实现一个完整的全栈项目,实现过程中发现共享的package会由于nestjs默认导出模块为commonjs
,而现在大部分前端项目都是vite构建的esm
模块,导致共享的package包就很难提供给不同模块的app共用。
查阅资料后找到一个不那么完美的解决方案,开发过程中始终别扭,然后在继续查阅资料的过程中,找到了今天的主角tsup、rollup
,它们可以打包一个package为cjs、esm
两种类型提供给app加载。
项目目录结构介绍
text
├── apps // apps(主要应用,admin为管理台ui、api为nestjs+fastify提供接口)
│ ├── admin
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── node_modules
│ │ ├── package.json
│ │ ├── public
│ │ ├── src
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ └── api
│ ├── README.md
│ ├── dist
│ ├── nest-cli.json
│ ├── node_modules
│ ├── package.json
│ ├── src
│ ├── test
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── node_modules
├── package.json
├── packages // packages(公共包,drizzle为api提供数据库orm支持、rest-contract提供api合约)
│ ├── drizzle
│ │ ├── drizzle.config.ts
│ │ ├── kit
│ │ ├── node_modules
│ │ ├── package.json
│ │ ├── src
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ └── rest-contract
│ └── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── turbo.json
package打包cjs和esm版本
tsup、rollup都可以打包cjs和esm模块,这里我暂时是用tsup实现的,以drizzle
共享包为例,tsup提供打包功能,package.json暴露不同模块提供外部使用。
text
├── drizzle
│ ├── drizzle.config.ts // drizzle-orm配置
│ ├── kit // drizzle-kit相关操作如seed播种数据操作
│ │ │ ├── db.ts
│ │ │ ├── mock
│ │ │ │ └── data.ts
│ │ │ └── seed.ts
│ ├── node_modules
│ ├── package.json // package配置
│ ├── src // drizzle-orm相关,如schema
│ │ │ ├── enum.ts
│ │ │ ├── index.ts
│ │ │ ├── schema
│ │ │ │ ├── base.schema.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── relation.schema.ts
│ │ │ │ └── system.schema.ts
│ │ │ └── utils
│ │ │ └── password.ts
│ ├── tsconfig.json // ts配置
│ └── tsup.config.ts // tsup配置
package.json
看注释位置
的主要配置,tsx
提供nodejs的ts直接运行环境,下面是配置。
json
{
"name": "@repo/drizzle",
"version": "0.0.1",
"description": "共享drizzle包",
// 主要配置:
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"types": {
"require": "./dist/index.d.cts",
"default": "./dist/index.d.ts"
},
"default": {
"require": "./dist/index.cjs",
"import": "./dist/index.js"
}
}
},
"scripts": {
"build:@repo/drizzle": "tsup src/*",
"dev": "tsup src/* --watch",
"db:migrate:deploy": "drizzle-kit migrate deploy",
"db:migrate:dev": "drizzle-kit migrate dev",
"db:push": "drizzle-kit push",
"db:generate": "drizzle-kit generate",
"studio": "drizzle-kit studio --verbose",
"seed": "tsx kit/seed.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/node": "^20.3.1",
"@types/pg": "^8.11.6",
"drizzle-kit": "^0.22.7",
"tsup": "^8.1.0",
"tsx": "^4.15.2"
},
"dependencies": {
"bcrypt": "^5.1.1",
"drizzle-orm": "^0.31.2",
"drizzle-zod": "^0.5.1",
"pg": "^8.12.0",
"zod": "^3.23.8"
}
}
tsup.json
tsup
提供打包cjs和esm
的打包功能,下面是配置。
json
import type { Options } from "tsup";
export const tsup: Options = {
entry: ["src/*.ts"],
format: ["cjs", "esm"],
dts: true,
// splitting: true,
splitting: false,
clean: true,
outDir: "dist",
sourcemap: "inline",
cjsInterop: true,
};
构建效果图
踩坑记录
不完善的cjs和esm处理方法
monorepo项目中,pnpm vite commonjs和esm不兼容处理方法
json
# 最简单的方法:
# 以shared-api为例,vite optimizeDeps配置,作用是:dev的时候vite预先把commonjs转换为esm包缓存在.vite目录
# 缺点是:如果shared-api包有修改,只能通过强制重新加载再次让vite编译esm缓存(暂时通过手动保存一下vite.config.ts实现再次强制编译)
# package.json内配置dev的时候加上--force参数
// package.json
{
"dev": "vite --force"
}
// vite.config.ts
{
optimizeDeps:{
include: {
// TODO: 由于加载的是commonjs,这里用vite预编译为esm模块
"@repo/shared-api"
}
},
build: {
// TODO: 由于加载的是commonjs,这里用vite预编译为esm模块
commonjsOptions: {
include: ["@repo/shared-api", "node_modules"],
},
}
}
另一种不完善方案:
packages打包为esm模块,nestjs利用webpack编译加载esm模块,类似于上面的vite方法,当共享包发生改变时,也无法立即生效。
相关链接
tsup.egoist.dev/#using-cust...