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

NestJS构建异常:dist目录嵌套src文件夹,如何恢复正常输出?

Nest.js构建产物嵌套src目录问题排查与修复

问题描述

原本使用"build:dev": "yarn nest build && cp package.json dist/package.json && cp -R prisma/schema dist/schema && cp .env.development dist/.env"命令构建Nest.js项目时,编译后的文件会直接输出到./dist目录,与src内部结构一致。但添加vitest及相关配置后,构建产物开始将整个src文件夹放入./dist(即dist/src/xxx),导致AWS EC2生产环境无法正常运行;同时部署后需要将.env重命名为.env.development才能执行Prisma迁移,引发数据库异常。

目录结构对比

  • 异常结构:dist目录下包含完整的src文件夹,编译文件位于dist/src/层级
  • 期望结构:编译后的文件直接存放在dist根目录,与原src内部层级完全对应

相关配置文件

package.json

{
  "name": "mypro-backend",
  "version": "1.0.0",
  "description": "mypro",
  "type": "commonjs",
  "scripts": {
    "start:dev": "NODE_ENV=development nest start --watch",
    "start:prod": "NODE_ENV=production nest start --watch",
    "start:debug": "nest start --debug --watch",
    "build:dev": "yarn nest build && cp package.json dist/package.json && cp -R prisma/schema dist/schema && cp .env.development dist/.env",
    "build:prod": "yarn nest build && cp package.json dist/package.json && cp -R prisma/schema dist/schema && cp .env.production dist/.env",
    "studio:dev": "dotenv -e .env.development -- yarn prisma studio --port 5558",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:cov": "vitest run --coverage",
    "test:debug": "vitest --inspect-brk --inspect --logHeapUsage --threads=false",
    "test:e2e": "dotenv -e .env.development -- vitest run --config ./vitest.config.e2e.ts"
  },
  "dependencies": {
    "@aws-sdk/client-s3": "^3.729.0",
    "@aws-sdk/s3-request-presigner": "^3.741.0",
    "@nestjs/common": "^10.0.0",
    "@nestjs/config": "^3.3.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/jwt": "^10.2.0",
    "@nestjs/passport": "^10.0.3",
    "@nestjs/platform-express": "^10.0.0",
    "@nestjs/throttler": "^6.3.0",
    "@prisma/client": "^6.2.1",
    "axios": "^1.7.9",
    "bcryptjs": "^2.4.3",
    "cookie-parser": "^1.4.7",
    "dotenv": "^16.4.7",
    "moment": "^2.30.1",
    "passport": "^0.7.0",
    "passport-jwt": "^4.0.1",
    "path": "^0.12.7",
    "pdfkit": "^0.16.0",
    "reflect-metadata": "^0.2.0",
    "rxjs": "^7.8.1",
    "uuid": "^11.0.5",
    "zod": "^3.24.1",
    "zod-validation-error": "^3.4.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^10.0.0",
    "@nestjs/schematics": "^10.0.0",
    "@nestjs/testing": "^10.0.0",
    "@rocketseat/eslint-config": "^2.2.2",
    "@swc/core": "^1.10.12",
    "@types/bcryptjs": "^2.4.6",
    "@types/express": "^5.0.0",
    "@types/multer": "^1.4.12",
    "@types/node": "^20.3.1",
    "@types/passport-jwt": "^4.0.1",
    "@types/pdfkit": "^0.13.9",
    "@types/supertest": "^6.0.2",
    "@typescript-eslint/eslint-plugin": "^8.0.0",
    "@typescript-eslint/parser": "^8.0.0",
    "@vitest/coverage-v8": "^3.0.5",
    "eslint": "^8.57.0",
    "prisma": "^6.2.1",
    "source-map-support": "^0.5.21",
    "supertest": "^7.0.0",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.1.3",
    "unplugin-swc": "^1.5.1",
    "vite-tsconfig-paths": "^5.1.4",
    "vitest": "^3.0.5"
  },
  "packageManager": "yarn@1.22.22"
}

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": true,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "types": [
      "vitest/globals",
      "Multer"
    ]
  }
}

tsconfig.build.json

{
  "extends": "./tsconfig.json",
  "exclude": [
    "node_modules",
    "test",
    "dist",
    "**/*spec.ts"
  ]
}

nest-cli.json

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true
  }
}

vitest.config.ts

import swc from 'unplugin-swc'
import { defineConfig } from 'vitest/config'
import tsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  test: {
    globals: true,
    root: './',
  },
  plugins: [
    tsConfigPaths(),
    swc.vite({
      module: { type: 'es6' },
    }),
  ],
})

vitest.config.e2e.ts

import swc from 'unplugin-swc'
import { defineConfig } from 'vitest/config'
import tsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
  test: {
    include: ['**/*.e2e-spec.ts'],
    globals: true,
    root: './',
    setupFiles: ['./test/setup-e2e.ts'],
  },
  plugins: [
    tsConfigPaths(),
    swc.vite({
      module: { type: 'es6' },
    }),
  ],
})

问题原因

添加vitest后,tsconfig.json中新增的"types": ["vitest/globals"]以及vite-tsconfig-paths插件干扰了Nest CLI的编译路径解析逻辑。加上tsconfig.json的baseUrl设为./,导致Nest CLI编译时错误地将整个src目录作为输入单元,而非将src下的内容作为编译源输出到dist根目录。

修复方案

方案1:明确指定编译源(最简修复)

修改tsconfig.build.json,添加include字段,强制仅编译src下的所有文件:

{
  "extends": "./tsconfig.json",
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "test",
    "dist",
    "**/*spec.ts"
  ]
}

方案2:强制Nest CLI的根目录配置

在nest-cli.json的compilerOptions中添加"rootDir": "./src",明确告诉Nest CLI以src为编译根目录,输出内容直接到dist:

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true,
    "rootDir": "./src"
  }
}

方案3:隔离测试与生产的TS配置(更规范)

创建单独的测试环境TS配置,避免vitest影响生产编译:

  1. 创建tsconfig.test.json:
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "types": ["vitest/globals", "Multer"]
  },
  "include": ["src/**/*", "test/**/*"]
}
  1. 修改tsconfig.json,移除types中的vitest/globals:
{
  "compilerOptions": {
    // ...保留其他原有配置
    "types": ["Multer"]
  }
}
  1. 更新两个vitest配置文件,指定使用测试专用TS配置:
// vitest.config.ts 和 vitest.config.e2e.ts 都需要修改
import swc from 'unplugin-swc'
import { defineConfig } from 'vitest/config'
import tsConfigPaths from 'vite-tsconfig-paths'
import path from 'path'

export default defineConfig({
  test: {
    // ...保留原有测试配置
  },
  plugins: [
    tsConfigPaths({ projects: ['./tsconfig.test.json'] }),
    swc.vite({
      module: { type: 'es6' },
    }),
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
})

验证修复

执行yarn build:dev后,检查dist目录结构,确认编译文件直接输出到dist根目录;同时测试Prisma迁移是否可通过.env正常执行。


内容的提问来源于stack exchange,提问作者Nilton Schumacher F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:05:55