Skip to content

Jest 无法转换模块问题:SyntaxError 不在模块外使用 import 语句

问题描述

在使用 Jest 测试 React + TypeScript + Webpack 项目时,经常会遇到以下错误:

bash
SyntaxError: Cannot use import statement outside a module

这个错误通常出现在 Jest 尝试测试包含 ES6 模块导入语法的文件时,特别是当这些文件位于 node_modules 目录中且未经过适当的转译处理时。

错误原因分析

Jest 默认不会转译 node_modules 中的文件,因为:

  1. 这些应该是已经转译好的 CommonJS 模块
  2. 转译所有 node_modules 会显著降低测试速度

然而,某些库可能:

  • 使用 ES6 模块语法(import/export)
  • 提供未转译的源代码
  • 需要特殊的转译配置

解决方案概览

以下是解决此问题的几种有效方法,从最简单到最复杂排列:

方案 1:使用 ts-jest 预设(推荐)

修改 Jest 配置以正确处理 TypeScript 和 ES 模块:

javascript
// 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文件:

json
// tsconfig.jest.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "allowJs": true
  }
}

方案 2:配置 Babel 正确处理模块

如果使用 Babel,确保正确配置:

javascript
// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', { 
      targets: { node: 'current' },
      modules: 'commonjs' // 关键配置
    }],
    '@babel/preset-react',
    '@babel/preset-typescript',
  ]
}

方案 3:启用 ESM 实验性支持

对于使用原生 ES 模块的项目:

  1. 在 package.json 中指定模块类型:
json
{
  "type": "module"
}
  1. 修改测试脚本:
json
{
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/.bin/jest"
  }
}
  1. 简化 Jest 配置:
javascript
// jest.config.js
export default { transform: {} };

方案 4:模块别名映射

对于特定模块(如 antd)的问题:

javascript
// jest.config.js
module.exports = {
  moduleNameMapper: {
    '^antd/es/(.*)$': 'antd/lib/$1',
    '^variables$': 'variables/dist/cjs'
  }
}

方案 5:终极解决方案 - 使用 Vitest

如果 Jest 配置过于复杂,可以考虑迁移到 Vitest:

bash
# 移除 Jest
npm uninstall jest ts-jest @types/jest

# 安装 Vitest
npm install -D vite vitest

配置 Vitest:

javascript
// vitest.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom'
  }
})

更新测试脚本:

json
{
  "scripts": {
    "test": "vitest"
  }
}

常见问题排查技巧

重要提示

在尝试任何解决方案前,请先清理缓存:

bash
npx jest --clearCache
  1. 检查模块类型:确认问题模块是否提供 CommonJS 版本
  2. 验证转译配置:确保所有需要转译的模块都在 transformIgnorePatterns 中排除
  3. 确认 TypeScript 配置:检查 allowJs 设置
  4. 查看文档:查阅相关库的文档,了解 Jest 集成建议

最佳实践

  1. 保持配置简单:尽量避免复杂的转译规则
  2. 使用社区预设:优先使用成熟的预设配置
  3. 定期更新依赖:许多 Jest 相关的问题在新版本中已修复
  4. 考虑替代方案:对于新项目,可以考虑使用 Vitest 作为 Jest 的替代品

结论

Jest 的模块转译问题通常可以通过正确配置 transformIgnorePatterns 和选择合适的预设来解决。对于复杂的项目,考虑使用 ts-jest/presets/js-with-ts 预设或迁移到 Vitest 可能是更简单的解决方案。

记住,每个项目的情况可能不同,可能需要结合多种方法才能彻底解决问题。建议从一个简单的配置开始,逐步调整直到找到适合自己项目的解决方案。