Node.js v20.11.1中ESM导入嵌套目录包时出现[ERR_UNSUPPORTED_DIR_IMPORT]错误的原因及解析逻辑咨询
嘿,这个问题我之前排查过,刚好能给你把Node.js ESM模块解析的关键逻辑讲明白~
核心原因:ESM与CommonJS的模块解析规则差异
Node.js的ESM模块系统和CommonJS在处理嵌套目录的package.json时,逻辑完全不一样:
- CommonJS会递归识别每个目录下的
package.json,哪怕是嵌套在某个包内部的子目录,只要目录里有main字段,require()就会自动把它当作一个子包入口来处理。 - ESM则只把
node_modules下的顶层包(比如你的test-a)当作完整的包来解析,嵌套在包内部的子目录对ESM来说只是普通目录,不会主动读取子目录里的package.json。
对应到你的场景来拆解
当你导入
import value from 'test-a'时:
ESM会去node_modules/test-a下找到根目录的package.json,读取main字段指向的index.js,并且会自动兼容CommonJS格式的导出,所以能正常拿到{ name: 'Jack' }。当你导入
import value from 'test-a/deep'时:
ESM把这个路径理解为test-a包下的deep子目录,而非一个独立的包。这时候它不会去读deep/package.json的main字段,而是遵循ESM的目录导入规则:ESM要求目录导入必须满足以下任一条件:
- 目录下存在
package.json且设置了"type": "module",同时有index.js/index.mjs作为入口 - 导入时明确指定文件名(比如
test-a/deep/index.js)
因为你的deep目录不符合这些要求,所以就抛出了[ERR_UNSUPPORTED_DIR_IMPORT]错误。
- 目录下存在
解决办法
给你几个实用的解决方案,按需选择:
方案一:明确指定导入文件名
直接在导入路径里加上文件名,比如:import value from 'test-a/deep/index.js'这种方式最简单,不需要修改任何配置。
方案二:在根包的package.json中配置exports字段
在test-a根目录的package.json里添加exports字段,手动映射嵌套路径:{ // 原有配置... "exports": { "./deep": "./deep/index.js" } }这是最符合ESM规范的方式,相当于给包的嵌套路径做了官方的入口声明,导入
test-a/deep就会自动解析到指定文件。方案三:将子目录改为ES模块格式
修改deep/index.js为ESM导出格式:export default { value: 'deep' }同时在
deep/package.json中添加"type": "module"字段,或者把文件名改成index.mjs,这样ESM就能识别目录导入了。不过这种方式需要调整代码格式,适合愿意统一为ESM的场景。
备注:内容来源于stack exchange,提问作者haoyu wang

