You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 16:13:10