纯ESM模式TS Node项目运行报MODULE_NOT_FOUND路径别名错误
纯ESM模式下TypeScript+Node.js路径别名失效修复方案
问题根因
报错核心是三类配置完全不适配原生ESM模块规范:
- 现有依赖里的
esm、module-alias、tsconfig-paths全部是基于CommonJS的模块劫持逻辑实现的,ESM模式下Node.js没有提供动态修改模块解析路径的CommonJS风格钩子,这三个包的别名解析逻辑完全不生效。 - ts-node配置里的
require字段仅在CommonJS模式下生效,ESM启动流程不存在require预加载阶段,写在该字段下的注册脚本根本不会执行。 - 现有
_moduleAliases配置全部指向编译后的build目录,开发环境直接跑ts源码时根本不存在该目录,就算别名逻辑生效也找不到对应文件。
分步修复操作
- 清理无效依赖和冗余配置
执行命令卸载不兼容的包:
打开package.json,删除npm uninstall esm module-alias tsconfig-pathsesm配置段、_moduleAliases配置段,删除start脚本里的-r esm -r module-alias/register参数;在package.json顶层添加"type": "module",这是Node.js识别项目为ESM模式的强制要求,也是top-level await能正常运行的前提。
打开tsconfig.json,删除ts-node配置下的require数组。 - 替换别名实现方案
纯ESM模式下不需要第三方路径别名包,直接用Node.js原生支持的imports字段实现别名,兼容性最好。
在package.json里添加imports配置,注意ESM别名强制要求以#开头:
同步修改tsconfig.json的编译配置,把模块相关配置改成NodeNext适配ESM规范,同时把paths配置和package.json的imports字段对齐:{ "imports": { "#config/*": "./config/*", "#repos/*": "./src/repos/*", "#models/*": "./src/models/*", "#shared/*": "./src/shared/*", "#server": "./src/server.ts", "#services/*": "./src/services/*", "#routes/*": "./src/routes/*", "#graphql/*": "./src/graphql/*" } }
全局替换项目源码里的import路径,把原来{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": false, "baseUrl": "./", "allowSyntheticDefaultImports": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "paths": { "#config/*": ["./config/*"], "#repos/*": ["./src/repos/*"], "#models/*": ["./src/models/*"], "#shared/*": ["./src/shared/*"], "#server": ["./src/server.ts"], "#services/*": ["./src/services/*"], "#routes/*": ["./src/routes/*"], "#graphql/*": ["./src/graphql/*"] }, "useUnknownInCatchVariables": false }, "include": ["src/**/*.ts", "spec/**/*.ts"], "exclude": ["src/public/", "node_modules"], "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" } }@开头的别名全部替换成#开头,比如import api from '@routes/api'改成import api from '#routes/api.js'。注意ESM模式下必须写全文件后缀,TS源码里导入本地文件统一写.js后缀,ts-node和TS编译器都能正常识别,编译后后缀也会自动对应。 - 调整启动命令
修改package.json里的nodemonConfig配置,把exec字段改成ESM模式的标准启动方式:
生产环境编译后,把package.json里imports字段的源码路径全部替换成build目录下的产物路径,比如{ "nodemonConfig": { "watch": ["src"], "ext": "ts, html", "ignore": ["src/public"], "exec": "node --loader ts-node/esm ./src/index.ts" } }./src/routes/*改成./build/routes/*,启动命令直接用node ./build/index.js --env=production即可,不需要加任何额外的预加载参数。
注意:如果用的是Node.js 18以下版本,需要升级到Node.js 18.13+,低版本对ESM的imports字段和ts-node ESM loader的兼容性存在已知问题。
内容的提问来源于stack exchange,提问作者Mustafak
相关产品推荐
相关产品推荐

