自定义ESM包导入遇ERR_UNSUPPORTED_DIR_IMPORT,如何让TSC构建时检测?
问题描述
我创建了一个仅支持ESM的包,未在package.json中定义exports字段,直接暴露所有文件。包的package.json配置如下:
{ "name": "@org/runtime", "version": "0.0.0-development", "description": "Runtime library", "module": "index.js", "type": "module", "scripts": { "b": "pnpm build", "prebuild": "rm -rf dist", "build": "tsc --project tsconfig.build.json && pnpm build:copy-dts && tsc-alias && cp package.json dist/", "build:copy-dts": "copyfiles -V -u 1 src/**/*.d.ts src/*.d.ts dist", "lint": "eslint .", "test": "vitest", "test:ci": "vitest --coverage", "release": "semantic-release" }, "devDependencies": { "tsc-alias": "^1.8.10", "typescript": "^5.5.3" }, "dependencies": {}, "engines": { "node": ">=22" } }
构建后的文件结构:
├── background-job │ ├── index.d.ts │ └── index.js ├── logger │ ├── formatter │ │ ├── console-formatter.d.ts │ │ ├── console-formatter.js │ │ ├── formatter.d.ts │ │ ├── formatter.js │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── json-formatter.d.ts │ │ └── json-formatter.js │ ├── index.d.ts │ ├── index.js │ ├── shared.d.ts │ └── shared.js ├── package.json ├── request-context │ ├── fastify.d.ts │ ├── fastify.js │ ├── index.d.ts │ ├── index.js │ ├── plugin.d.ts │ └── plugin.js ├── result.d.ts ├── result.js ├── tests │ ├── setup.d.ts │ └── setup.js ├── types.d.ts ├── validations │ ├── is-truthy.d.ts │ └── is-truthy.js └── validator ├── index.d.ts ├── index.js ├── schema-extensions.d.ts ├── schema-extensions.js └── yup.d.ts
主项目的tsconfig.json配置:
{ "compilerOptions": { "target": "ES2023", "module": "NodeNext", "esModuleInterop": true, "moduleResolution": "NodeNext", "lib": [ "ESNext" ], "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "outDir": "dist", "rootDir": "src", "baseUrl": ".", "typeRoots": [ "node_modules/@types" ], "types": [ "node" ], "declaration": false, "sourceMap": false, "paths": { "@/*": [ "src/*" ] } }, "include": [ "src/**/*.ts", "node_modules/@types/node/globals.d.ts", "node_modules/vitest/globals.d.ts", "node_modules/.pnpm/vitest@2.0.5_@types+node@20.16.1/node_modules/vitest/globals.d.ts" ], "exclude": [ "node_modules", "dist" ], "tsc-alias": { "resolveFullPaths": true, "verbose": true } }
安装包后,尝试导入:
import { Logger } from '@org/runtime/logger';
ESLint、IDE和tsc均无警告或错误,能正常完成lint和构建,但运行转译后的代码时出现错误:
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/my-project/node_modules/@org/runtime/logger' is not supported resolving ES modules imported from /my-project/index.js Did you mean to import "@org/runtime/logger/index.js"?
添加index.js后缀后可正常运行,希望不依赖ESLint,让TSC在构建阶段或IDE提前识别该问题,请问该如何处理?
解决方案
1. 给包添加exports字段(推荐)
在包的package.json中定义exports,明确指定各个子路径的入口,统一TypeScript和Node.js的解析规则,消除目录导入的歧义。
修改包的package.json,添加如下内容:
"exports": { ".": "./index.js", "./logger": "./logger/index.js", "./logger/*": "./logger/*", "./background-job": "./background-job/index.js", "./request-context": "./request-context/index.js", "./validations/*": "./validations/*", "./validator": "./validator/index.js", "./result": "./result.js", "./types": "./types.d.ts" }
- 配置后,
@org/runtime/logger会被明确映射到logger/index.js,TypeScript编译时会自动生成带正确后缀的导入语句。 - IDE会根据
exports定义提供准确的路径提示,避免错误的目录导入写法。
2. 配置TypeScript启用严格模块解析
在主项目的tsconfig.json的compilerOptions中,启用严格模块解析规则,让TSC和IDE检查导入路径是否符合ESM规范:
"compilerOptions": { // 保留原有配置 "strictModuleResolution": true, "moduleSuffixes": [".js", ".ts"] }
strictModuleResolution会强制TSC按照Node.js的ESM解析逻辑检查路径,不符合规范的导入会直接报错。moduleSuffixes指定TSC优先识别的文件后缀,帮助IDE自动补全正确的导入路径。
3. 优化包的TypeScript构建配置
在包的tsconfig.build.json中添加以下配置,生成更准确的类型文件,提升IDE和主项目TSC的识别能力:
"compilerOptions": { // 保留原有配置 "declaration": true, "declarationMap": true, "moduleResolution": "NodeNext", "strictModuleResolution": true }
declarationMap会生成类型映射文件,让IDE可以直接关联到包的源码路径,提供更精准的导入提示。
4. 临时方案:直接导入带后缀的文件
如果暂时无法修改包的配置,可以在代码中直接导入带完整后缀的文件:
import { Logger } from '@org/runtime/logger/index.js';
这种方式虽能解决运行错误,但不够优雅,仅作为临时过渡方案。
内容的提问来源于stack exchange,提问作者Jose Truyol
相关产品推荐
相关产品推荐

