PNPM Monorepo中NestJS集成Prisma的ESM兼容问题解决
问题场景
我搭建了一个包含两个NestJS应用(apps/api和apps/backend,两者完全相同)与一个Prisma数据库包(packages/database)的PNPM monorepo。运行api的start:dev命令时,出现如下错误:
(node:69498) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. (Use `node --trace-warnings ...` to show where the warning was created) /Users/username/Documents/Slug/slug-monorepo/packages/database/src/index.ts:1 export * from "@prisma/client"; ^^^^^^ SyntaxError: Unexpected token 'export' at wrapSafe (node:internal/modules/cjs/loader:1389:18) at Module._compile (node:internal/modules/cjs/loader:1425:20) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1564:10) at Module.load (node:internal/modules/cjs/loader:1287:32) at Function.Module._load (node:internal/modules/cjs/loader:1103:12) at Module.require (node:internal/modules/cjs/loader:1310:19) at require (node:internal/modules/helpers:179:18) at Object.<anonymous> (/Users/username/Documents/Slug/slug-monorepo/apps/api/src/prisma/prisma.service.ts:2:1) at Module._compile (node:internal/modules/cjs/loader:1480:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1564:10)
问题原因
核心问题在于packages/database的package.json中,main字段直接指向了未编译的TS源码文件src/index.ts。NestJS的start:dev模式依赖ts-node运行,但ts-node在加载monorepo中的本地包时,不会自动编译依赖的TS文件,导致Node.js的CJS加载器直接读取TS文件的export语法,从而抛出语法错误。
解决方案
1. 修正数据库包的package.json配置
更新packages/database/package.json,将入口指向编译后的JS文件,并添加类型声明路径:
{ "name": "@slug-monorepo/database", "version": "1.0.0", "description": "", "main": "dist/index.js", // 改为编译后的JS文件 "types": "dist/index.d.ts", // 添加类型声明路径,提供TS类型提示 "scripts": { "build": "tsc" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@types/node": "^20.14.8", "prisma": "^5.16.0", "typescript": "^5.1.3" }, "dependencies": { "@prisma/client": "^5.16.0" } }
2. 确保数据库包先编译再被应用引用
在根目录的package.json中添加编译数据库包的脚本,并在API的启动脚本中前置执行:
// 根目录 package.json { "scripts": { "build:database": "pnpm --filter @slug-monorepo/database build" } }
更新apps/api/package.json的启动脚本,确保启动前先编译数据库包:
{ "scripts": { "start:dev": "pnpm build:database && nest start --watch", // 其他脚本保持不变 } }
如果需要支持热更新时自动重新编译数据库包,可以添加watch脚本到packages/database/package.json:
{ "scripts": { "build": "tsc", "watch": "tsc --watch" } }
之后可以在另一个终端运行pnpm --filter @slug-monorepo/database watch,实现数据库包源码变更时自动编译。
3. 修正应用中的导入路径错误
检查应用代码中的导入路径,确保与数据库包的name字段一致。比如apps/api/src/prisma/prisma.service.ts和createSlug.dto.ts中,当前导入的是@slug/database,需要改为@slug-monorepo/database:
// apps/api/src/prisma/prisma.service.ts import { Injectable, OnModuleInit } from '@nestjs/common'; import { PrismaClient } from '@slug-monorepo/database'; // 修正路径 @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect(); } }
// apps/api/src/slug/dto/createSlug.dto.ts import { SlugType } from '@slug-monorepo/database'; // 修正路径 export class CreateSlugDto { @IsOptional() @IsEnum(SlugType) slug?: SlugType; }
4. 验证配置
执行以下步骤验证:
- 运行
pnpm build:database编译数据库包 - 运行
pnpm --filter @slug-monorepo/api start:dev启动API服务 - 检查是否正常连接数据库,且无语法错误
内容的提问来源于stack exchange,提问作者Dimitri Borgers

