Jest 无法转换模块问题:SyntaxError 不在模块外使用 import 语句
问题描述
在使用 Jest 测试 React + TypeScript + Webpack 项目时,经常会遇到以下错误:
SyntaxError: Cannot use import statement outside a module
这个错误通常出现在 Jest 尝试测试包含 ES6 模块导入语法的文件时,特别是当这些文件位于 node_modules
目录中且未经过适当的转译处理时。
错误原因分析
Jest 默认不会转译 node_modules
中的文件,因为:
- 这些应该是已经转译好的 CommonJS 模块
- 转译所有 node_modules 会显著降低测试速度
然而,某些库可能:
- 使用 ES6 模块语法(import/export)
- 提供未转译的源代码
- 需要特殊的转译配置
解决方案概览
以下是解决此问题的几种有效方法,从最简单到最复杂排列:
方案 1:使用 ts-jest 预设(推荐)
修改 Jest 配置以正确处理 TypeScript 和 ES 模块:
// jest.config.js
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'node',
transformIgnorePatterns: [
'node_modules/(?!variables/.*)'
],
globals: {
'ts-jest': {
tsconfig: 'tsconfig.jest.json' // 可选的独立配置
}
}
}
同时确保 TypeScript 配置允许处理 JavaScript文件:
// tsconfig.jest.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"allowJs": true
}
}
方案 2:配置 Babel 正确处理模块
如果使用 Babel,确保正确配置:
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: { node: 'current' },
modules: 'commonjs' // 关键配置
}],
'@babel/preset-react',
'@babel/preset-typescript',
]
}
方案 3:启用 ESM 实验性支持
对于使用原生 ES 模块的项目:
- 在 package.json 中指定模块类型:
{
"type": "module"
}
- 修改测试脚本:
{
"scripts": {
"test": "node --experimental-vm-modules node_modules/.bin/jest"
}
}
- 简化 Jest 配置:
// jest.config.js
export default { transform: {} };
方案 4:模块别名映射
对于特定模块(如 antd)的问题:
// jest.config.js
module.exports = {
moduleNameMapper: {
'^antd/es/(.*)$': 'antd/lib/$1',
'^variables$': 'variables/dist/cjs'
}
}
方案 5:终极解决方案 - 使用 Vitest
如果 Jest 配置过于复杂,可以考虑迁移到 Vitest:
# 移除 Jest
npm uninstall jest ts-jest @types/jest
# 安装 Vitest
npm install -D vite vitest
配置 Vitest:
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom'
}
})
更新测试脚本:
{
"scripts": {
"test": "vitest"
}
}
常见问题排查技巧
重要提示
在尝试任何解决方案前,请先清理缓存:
npx jest --clearCache
- 检查模块类型:确认问题模块是否提供 CommonJS 版本
- 验证转译配置:确保所有需要转译的模块都在
transformIgnorePatterns
中排除 - 确认 TypeScript 配置:检查
allowJs
设置 - 查看文档:查阅相关库的文档,了解 Jest 集成建议
最佳实践
- 保持配置简单:尽量避免复杂的转译规则
- 使用社区预设:优先使用成熟的预设配置
- 定期更新依赖:许多 Jest 相关的问题在新版本中已修复
- 考虑替代方案:对于新项目,可以考虑使用 Vitest 作为 Jest 的替代品
结论
Jest 的模块转译问题通常可以通过正确配置 transformIgnorePatterns
和选择合适的预设来解决。对于复杂的项目,考虑使用 ts-jest/presets/js-with-ts
预设或迁移到 Vitest 可能是更简单的解决方案。
记住,每个项目的情况可能不同,可能需要结合多种方法才能彻底解决问题。建议从一个简单的配置开始,逐步调整直到找到适合自己项目的解决方案。