Docker化pnpm Monorepo报错:找不到模块'@/libs/shared/middlewares/error.middleware'
问题:Docker Compose运行pnpm workspace的MERN monorepo时模块找不到
我正在开发一个用pnpm workspaces管理的MERN monorepo,想把整个环境用Docker Compose容器化。本地运行完全正常,但执行docker compose up时出现模块找不到的错误。
项目结构
mern-monorepo/ api/ client/ docker-compose.yml package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.json
根目录tsconfig.json配置
{ "compilerOptions": { /* 项目设置 */ "target": "es2022", "lib": ["dom", "esnext", "es2022"], "allowJs": true, "outDir": "./dist", "sourceMap": true, "removeComments": true, "isolatedModules": true, "downlevelIteration": true, "skipLibCheck": true, /* 模块解析 */ "module": "NodeNext", "esModuleInterop": true, "moduleResolution": "NodeNext", "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { "@/*": ["api/src/*", "client/src/*"] }, /* 检查器配置 */ "strict": true, "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, /* 实验选项 */ "emitDecoratorMetadata": true, "experimentalDecorators": true, /* 高级选项 */ "forceConsistentCasingInFileNames": true }, "types": ["vitest/globals"], "include": [ "**/*.ts", "**/*.tsx", "api/vite.config.ts", "client/vitest.config.ts", "vitest.config.mts", "vitest.workspace.mts" ], "exclude": [ "node_modules", "**/node_modules/*", "**/dist/*", "./client/cypress.config.ts" ] }
API的package.json配置
{ "name": "@mern-monorepo/api", "version": "1.0.0", "description": "Node and Express server API", "main": "index.js", "license": "MIT", "scripts": { "start:dev": "pnpm tsx watch src/server.ts", "start": "pnpm build && node dist/server.js", "build": "tsc && tsc-alias", "deploy:clean": "pm2 stop all && pm2 delete all", "deploy:dev": "pm2 start ecosystem.config.js --only dev && pm2 logs all", "deploy:prod": "pnpm build && pm2 start ecosystem.config.js --only prod", "test": "dotenv -e .env.development.test -- npx vitest" }, "dependencies": { "bcrypt": "^5.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "compression": "^1.7.4", "cookie-parser": "^1.4.6", "cookie-session": "^2.0.0", "cors": "^2.8.5", "dotenv": "^16.1.4", "express": "^4.18.2", "helmet": "^7.0.0", "hpp": "^0.2.3", "http-status-codes": "^2.2.0", "ioredis": "^5.3.2", "jsonwebtoken": "^9.0.0", "mongoose": "^8.4.4", "winston": "^3.9.0" }, "devDependencies": { "@types/bcrypt": "^5.0.0", "@types/compression": "^1.7.2", "@types/cookie-parser": "^1.4.3", "@types/cookie-session": "^2.0.44", "@types/cors": "^2.8.13", "@types/express": "^4.17.17", "@types/hpp": "^0.2.2", "@types/jsonwebtoken": "^9.0.2", "@types/node": "^20.2.5", "@types/supertest": "^6.0.2", "@types/swagger-jsdoc": "^6.0.1", "@types/swagger-ui-express": "^4.1.3", "dotenv-cli": "^7.2.1", "nodemon": "^3.1.4", "pm2": "^5.3.0", "supertest": "^7.0.0", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.1", "tinyspy": "^3.0.0", "ts-node": "^10.9.1", "tsc-alias": "^1.8.6", "tsconfig-paths": "^4.2.0", "tsx": "^4.16.0", "typescript-transform-paths": "^3.4.7" }, "config": { "commitizen": { "path": "cz-conventional-changelog" } }, "engines": { "node": ">=22.0.0", "pnpm": ">=9.4.0" }, "packageManager": "pnpm@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a" }
API的Dockerfile
FROM node:alpine RUN apk update && apk add --no-cache nodejs && corepack enable WORKDIR /api COPY package.json tsconfig.json ./ COPY src ./src RUN ls -a RUN pnpm install EXPOSE 4000 CMD [ "pnpm", "start:dev" ]
API的Docker Compose文件
services: api: container_name: api image: api build: context: . dockerfile: Dockerfile.dev restart: always ports: - 4000:4000 - 5173:5173 environment: - NODE_ENV=development - BASE_URL=http://localhost:4000 volumes: - .:/api - node_modules:/api/node_modules
根目录Docker Compose文件
services: api: extends: service: api file: api/docker-compose.yaml volumes: node_modules: networks: mern: name: mern
错误信息
api | > @mern-monorepo/api@1.0.0 start:dev /api api | > pnpm tsx watch src/server.ts api | api | node:internal/modules/cjs/loader:1219 api | const err = new Error(message); api | ^ api | api | Error: Cannot find module '@/libs/shared/middlewares/error.middleware' api | Require stack: api | - /api/src/app/index.ts api | - /api/src/server.ts api | at Module._resolveFilename (node:internal/modules/cjs/loader:1219:15) api | at resolve (/api/node_modules/.pnpm/tsx@4.16.2/node_modules/tsx/dist/register-CCR7NebB.cjs:2:554) api | at resolveRequest (/api/node_modules/.pnpm/tsx@4.16.2/node_modules/tsx/dist/register-CCR7NebB.cjs:1:2758) api | at /api/node_modules/.pnpm/tsx@4.16.2/node_modules/tsx/dist/register-CCR7NebB.cjs:2:583 api | at m._resolveFilename (file:///api/node_modules/.pnpm/tsx@4.16.2/node_modules/tsx/dist/register-Cehy_bZS.mjs:1:789) api | at Module._load (node:internal/modules/cjs/loader:1045:27) api | at TracingChannel.traceSync (node:diagnostics_channel:315:14) api | at wrapModuleLoad (node:internal/modules/cjs/loader:215:24) api | at Module.require (node:internal/modules/cjs/loader:1304:12) api | at require (node:internal/modules/helpers:123:16) { api | code: 'MODULE_NOT_FOUND', api | requireStack: [ '/api/src/app/index.ts', '/api/src/server.ts' ] api | } api | api | Node.js v22.4.0
解决方案
问题核心是Docker容器内无法解析tsconfig的@/*路径别名,同时pnpm workspace的目录结构未正确映射,修复步骤如下:
1. 调整Dockerfile,复制根目录配置
当前Dockerfile仅复制api目录内容,缺少根目录的tsconfig和pnpm workspace配置,修改后:
FROM node:alpine RUN apk update && apk add --no-cache nodejs && corepack enable # 创建monorepo根目录作为工作目录 WORKDIR /mern-monorepo # 复制根目录核心配置文件 COPY package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.json ./ # 复制api子目录所有内容 COPY api ./api # 切换到api目录 WORKDIR /mern-monorepo/api RUN pnpm install EXPOSE 4000 CMD [ "pnpm", "start:dev" ]
2. 修改API的Docker Compose配置
将构建上下文改为根目录,并调整卷映射路径:
services: api: container_name: api image: api build: context: .. # 构建上下文指向monorepo根目录 dockerfile: api/Dockerfile.dev restart: always ports: - 4000:4000 - 5173:5173 environment: - NODE_ENV=development - BASE_URL=http://localhost:4000 volumes: - ../:/mern-monorepo # 映射整个monorepo到容器 - /mern-monorepo/api/node_modules # 避免本地node_modules覆盖容器内依赖
3. 让tsx识别路径别名
tsx默认不读取tsconfig的路径映射,修改API的package.json启动脚本,指定根目录的tsconfig:
"scripts": { "start:dev": "pnpm tsx --tsconfig ../../tsconfig.json watch src/server.ts", // 其他脚本保持不变 }
4. 验证pnpm workspace可用性
容器内已复制根目录的pnpm-workspace.yaml,pnpm会自动识别workspace依赖,无需额外配置。
本地正常的原因
本地在monorepo根目录运行pnpm,tsconfig的baseUrl: .指向根目录,@/*能正确解析到api/src/*;但之前容器内工作目录是/api,缺少根目录配置,导致路径别名失效。
内容的提问来源于stack exchange,提问作者Francis Lagares
相关产品推荐
相关产品推荐

