如何配置ts-jest实现CommonJS动态导入ESM模块的测试?
问题场景
现有一个基于CommonJS模块系统的库,通过以下动态导入函数加载仅支持ESM的模块(目的是避免TypeScript将import语法替换为require):
export async function loadEsmModule<T>(modulePath: string|URL): Promise<T> { const namespaceObject = (await new Function('modulePath', `return import(modulePath);`)(modulePath)); // 若目标模块不是ESM,所需值会存储在`default`属性中 if (namespaceObject.default) { return namespaceObject.default; } else { return namespaceObject; } }
使用ts-jest运行该库的CommonJS单元测试时,尝试多种配置组合均失败:
- 启用Node.js实验性选项
--experimental-vm-modules:触发worker进程崩溃,报错"A jest worker process (pid=2701) was terminated by another process: signal=SIGSEGV, exitCode=null. Operating system logs may contain more information on why this occurred." - 使用Jest选项
--runInBand:报错"Test environment has been torn down" - 调大
maxWorkers和maxConcurrency参数:无任何改善 - 切换不同ts-jest预设:问题依旧
可行配置方案
针对该混合模块场景,以下配置组合可解决测试运行问题:
1. Jest配置文件(jest.config.js)
采用CommonJS格式配置,明确指定CommonJS模块模式,适配ts-jest的转换规则:
module.exports = { preset: 'ts-jest/presets/js-with-ts', testEnvironment: 'node', moduleFileExtensions: ['ts', 'js', 'json'], transform: { '^.+\\.tsx?$': ['ts-jest', { useESM: false, // 强制ts-jest以CommonJS模式编译TypeScript代码 isolatedModules: true }] }, // 禁用Jest自动识别ESM的逻辑,避免模块类型混淆 extensionsToTreatAsEsm: [], // 强制Jest使用CommonJS模块系统处理所有文件 moduleType: 'commonjs', // 可选:针对特定ESM依赖添加路径映射(若依赖导入路径存在问题) moduleNameMapper: { // 示例:映射ESM依赖的入口文件 // '^some-esm-lib$': '<rootDir>/node_modules/some-esm-lib/index.js' } };
2. 测试运行命令
在package.json的scripts中配置测试命令,不要启用--experimental-vm-modules(该实验性特性在混合模块场景下稳定性极差):
{ "scripts": { "test": "jest --verbose" } }
3. 关键配置说明
preset: 'ts-jest/presets/js-with-ts':该预设专为CommonJS项目设计,会将TypeScript代码编译为CommonJS格式,同时兼容JavaScript文件。useESM: false:禁止ts-jest启用ESM转换模式,确保编译后的测试代码和库代码均为CommonJS,避免与动态导入的ESM模块产生模块系统冲突。extensionsToTreatAsEsm: []:关闭Jest自动将.js或.ts文件识别为ESM的逻辑,防止Jest误判模块类型引发错误。- 放弃
--experimental-vm-modules:该选项是导致worker进程崩溃的核心原因,Jest在处理混合模块时对该实验性特性的支持尚未成熟,完全禁用即可避免崩溃问题。
4. 测试文件编写规范
测试文件保持CommonJS风格(或让ts-jest自动转换为CommonJS),避免在测试代码中直接使用ESM语法:
// 测试文件示例 const { loadEsmModule } = require('../src/your-library'); test('成功加载ESM模块', async () => { const esmModule = await loadEsmModule('target-esm-module'); expect(esmModule).toBeDefined(); });
内容的提问来源于stack exchange,提问作者daniel-sc
相关产品推荐
相关产品推荐

