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

使用bundler resolution构建正常但运行报错ERR_MODULE_NOT_FOUND

ERR_MODULE_NOT_FOUND 问题排查与解决(切换到bundler resolution后启动失败)

将项目切换为bundler模块解析策略后,tsc构建可正常完成,但启动Node.js应用时出现Error [ERR_MODULE_NOT_FOUND],提示无法找到模块/home/node/app/dist/websocket/config/setup。已配置TypeScript采用bundler resolution,按规则无需添加文件扩展名,现分析问题原因及解决方法。

报错日志

node:internal/errors:496
│     ErrorCaptureStackTrace(err);
│     ^
│
│ Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/node/app/dist/websocket/config/setup' imported from /home/node/app/dist/app.js
│     at new NodeError (node:internal/errors:405:5)
│     at finalizeResolution (node:internal/modules/esm/resolve:327:11)
│     at moduleResolve (node:internal/modules/esm/resolve:980:10)
│     at defaultResolve (node:internal/modules/esm/resolve:1206:11)
│     at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:404:12)
│     at ModuleLoader.resolve (node:internal/modules/esm/loader:373:25)
│     at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:250:38)
│     at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:39)
│     at link (node:internal/modules/esm/module_job:75:36) {
│   url: 'file:///home/node/app/dist/websocket/config/setup',
│   code: 'ERR_MODULE_NOT_FOUND'
│ }
│
│ Node.js v18.20.6

tsconfig.json

{
    "compilerOptions": {
      "target": "ES2020",
      "module": "esnext",
      "moduleResolution": "bundler",
      "outDir": "./dist",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true,
      "declaration": true,
    },
    "include": ["src/**/*"],
    "files": ["src/app.ts"]
  }

package.json

{
  "name": "texhub-broadcast",
  "version": "1.0.19",
  "description": "",
  "main": "./dist/app.js",
  "module": "./dist/app.js",
  "types": "./dist/app.d.ts",
  "type":"module",
  "exports": {
    "./dist/websocket/conn/socket_io_client_provider": "./dist/websocket/conn/socket_io_client_provider.js",
    ".": {
      "import": "./dist/app.js"
    }
  },
  "files": [
    "dist/*"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint --fix",
    "dev": "vite-node src/app.ts",
    "build": "npx tsc",
    "dist": "npx tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^16.4.7",
    "express": "^4.21.2",
    "flatted": "^3.3.2",
    "globals": "^15.14.0",
    "lib0": "^0.2.99",
    "lodash": "^4.17.21",
    "log4js": "^6.9.1",
    "meilisearch": "^0.35.1",
    "prom-client": "^14.2.0",
    "socket.io": "^4.8.1",
    "socket.io-client": "^4.8.1",
    "ws": "^8.18.0",
    "y-leveldb": "^0.1.2",
    "y-protocols": "^1.0.6",
    "y-websocket": "^1.5.0",
    "yjs": "^13.6.23"
  },
  "devDependencies": {
    "@types/express": "^5.0.0",
    "@types/lodash": "^4.17.15",
    "@types/node": "^22.12.0",
    "@types/ws": "^8.5.14",
    "@typescript-eslint/eslint-plugin": "^8.22.0",
    "@typescript-eslint/parser": "^8.22.0",
    "eslint": "^9.19.0",
    "typescript": "^5.7.3",
    "vite": "^6.0.11",
    "vite-node": "^3.0.4",
    "vitest": "^3.0.4"
  }
}

导入代码

import { setupWSConnection } from "./websocket/config/setup";

目录结构

├── dist
│   ├── websocket
|        |__ config
|              |_ setup.js
│   └── app.js
├── package-lock.json
├── package.json
├── src
│   ├── websoccket
|         |_ config
|               |_ setup.ts  
│   └── app.ts
└── tsconfig.json

问题原因

1. Node.js ESM 不兼容 bundler resolution 规则

TypeScript 的moduleResolution: "bundler"是为 Vite、Webpack 等打包工具设计的,这类工具会自动处理无扩展名的导入逻辑。但 Node.js 原生 ESM 要求导入必须明确指定文件扩展名(如.js),tsc 编译后生成的导入语句仍然保留无扩展名的写法,导致 Node.js 无法匹配到实际的.js文件。

2. 目录拼写错误(潜在问题)

从目录结构看,src下的目录名为websoccket(多了一个c),但导入路径写的是./websocket/config/setup。虽然当前构建未报错,但这属于路径不匹配的隐患,可能导致后续维护或构建异常。


解决方法

方法一:切换到 Node.js 兼容的模块解析策略

修改tsconfig.json中的module和moduleResolution配置,让 TypeScript 生成符合 Node.js ESM 规则的代码(自动添加扩展名):

{
  "compilerOptions": {
    "module": "NodeNext", // 或 Node16
    "moduleResolution": "NodeNext", // 与module配置保持一致
    // 其他原有配置不变
  }
}

重新执行npm run build后,生成的dist/app.js中的导入语句会自动追加.js扩展名,Node.js 可正常解析文件。

方法二:使用 Vite 打包替代纯 tsc 编译

利用项目已依赖的 Vite 处理 bundler 规则的导入,生成可直接运行的产物:

  1. 在项目根目录创建vite.config.ts:
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    outDir: 'dist',
    lib: {
      entry: 'src/app.ts',
      formats: ['es'],
      fileName: 'app'
    },
    rollupOptions: {
      // 排除依赖包,避免打包进产物
      external: Object.keys(require('./package.json').dependencies)
    }
  }
});
  1. 修改package.json的build脚本:
"scripts": {
  "build": "vite build",
  // 其他脚本保持不变
}

执行npm run build后,Vite 会自动处理无扩展名导入的问题,生成符合 Node.js 运行要求的产物。

方法三:手动添加导入扩展名

如果坚持使用bundler解析策略,可手动给所有导入语句添加.js扩展名(注意是编译后的.js,而非源文件的.ts):

import { setupWSConnection } from "./websocket/config/setup.js";

这种方法虽繁琐,但可让 Node.js 直接运行 tsc 编译后的代码。

额外修复:目录拼写错误

将src下的websoccket目录重命名为websocket,保持导入路径与实际目录结构一致,消除路径匹配隐患。


内容的提问来源于stack exchange,提问作者Dolphin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:05:58