Jest无法转换该模块 - SyntaxError:无法在模块外部使用导入语句。

155

无论我尝试了什么,我都无法摆脱这个错误:SyntaxError: Cannot use import statement outside a module,真是令人沮丧。这里有没有人解决了这个问题?我已经阅读了数百篇的stackoverflow和github帖子,但没有明确的解决方案。

这是一个React、Typescript、Webpack项目。我正在尝试测试一个模块,但是Jest不知何故无法将模块转换为纯JavaScript

我收到的错误消息是:

/Users/me/dev/Project/project/node_modules/variables/src/variables.js:12
    import './main.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      17 | 
      18 | */
    > 19 | import { GlobalVars } from 'variables'
         | ^
      20 | 
      21 | export const Vars = new GlobalVars()
      22 | 

我尝试过但无法解决的方法:

  • babel.config中使用 env 的设置:env.test.preset: ['@babel/plugin-transform-modules-commonjs']

  • 修改 Jest 配置中的transform 设置为 '^.+\\.jsx?$': 'babel-jest','^.+\\.tsx?$': 'ts-jest',以及其他所有可能性。

  • Jest 配置中的 testPathIgnorePatternstransformIgnorePatterns

  • 使用.babel.config.js而不是.babelrc.js

...等等。

我的配置如下:

package.json

  "jest": {
    "preset": "ts-jest",
    "testEnvironment": "node"
  }

.babelrc.js

module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }],
    '@babel/preset-react',
    '@babel/preset-typescript',
  ],
  plugins: [
    '@babel/plugin-transform-runtime',
    '@babel/proposal-class-properties',
    '@babel/transform-regenerator',
    '@babel/plugin-transform-template-literals',
    'react-hot-loader/babel',
  ],
}

变量.ts

import { GlobalVars } from 'variables'

export const Vars = new GlobalVars()

variables.spec.ts

import { Vars } from './variables.ts'

describe('Test The Package', () => {
  it('Should accept new variables', () => {
    Vars.newVariable = 'new variable'
    expect(Vars.newVariable).toEqual('new variable')
  })
})

有解决这个问题的想法吗?


1
尝试使用 --experimental-vm-modules 在 Node 中运行实验性 ESM,就像这个答案中所示:https://stackoverflow.com/a/70200697/5078874 - Adrian Escutia Soto
18个回答

111

虽然我已经单独尝试过它们,但我还没有尝试过它们的组合(transformtransformIgnorePatterns)。因此,这个jest配置解决了我的问题:

  "jest": {
    "preset": "ts-jest",
    "testEnvironment": "node",
    "transform": {
      "node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"
    },
    "transformIgnorePatterns": [
      "node_modules/(?!variables/.*)"
    ]
  },

我的错误是:

  1. 没有同时使用 transformtransformIgnorePatterns
  2. babel-jest 定义为转换器,而不是 ts-jest(我想这可能是当 jest 的 preset 被定义为 ts-jest 时的问题。因为如果我将其更改为 babel-jest,它会再次抛出相同的错误):
--- "node_modules/variables/.+\\.(j|t)sx?$": "babel-jest"
+++ "node_modules/variables/.+\\.(j|t)sx?$": "ts-jest"

2
对于使用babel而不是ts-jest的人,请将您的.babelrc重命名为babel.config.json,参见:https://babeljs.io/docs/en/configuration#whats-your-use-case这样,如果您已经在使用babel,则可能不需要额外的转换。 - Ville
1
哪个 jest 配置文件?有一个 jest.config.js 文件,还有一个 package.json 中的 jest 配置部分。这个配置确切地位于哪里? - Oliver Watkins
太棒了!我已经在 package.json 中完成了这个配置,而且它起作用了。谢谢。 - jfk
3
我是jest的新手,但我感觉我已经尝试了互联网上的每个建议,但这个错误仍然存在。我有一种沉重的感觉,jest v26和v27之间的事情发生了变化。我也尝试了ts-jest。 - Jarrod Smith
我不需要添加任何与transformtransformIgnorePatterns相关的内容,但是缺少了"preset": "ts-jest""testEnvironment": "node",一旦我添加了这些,我的测试就可以工作了,谢谢! - Issung
显示剩余4条评论

32

我的问题与众不同,因为在 node_modules 中的一个依赖项的 .js 文件中,jest 会遇到 SyntaxError: Cannot use import statement outside a module 的错误。

我必须确保 ts-jest 在转换时不会忽略有问题的依赖项中的 .js 文件。

仔细阅读了有关 预设 的内容后,我意识到,使用 preset: 'ts-jest' 将它们保留为原样。我将其更改为 preset: 'ts-jest/presets/js-with-ts' 并在 tsconfig.json 中设置 "allowJs": true

为了不捣乱项目的 tsconfig.json,我为 jest 使用单独的配置文件。

最终,我的 jest.config.js 主要看起来像这样:

module.exports = {
    preset: 'ts-jest/presets/js-with-ts',
    testEnvironment: "node",
    globals: {
        'ts-jest': {
            tsconfig: '<rootDir>/test/tsconfig.json',
        },
    },
    transformIgnorePatterns: [
        "node_modules/(?!troublesome-dependency/.*)",
    ],
}

顺便提一下,我不需要一个transform字段,因为预设已经包含在内了。

再说一遍,我不需要引入任何babel配置。


2
将预设从ts-jest更改为ts-jest/presets/js-with-ts就足以让我在我正在迁移到TypeScript的旧代码上运行jest。谢谢! - Arajay

8

使用Vitest。

我尝试了这里提出的大部分建议。我发现Jest安装起来相当痛苦,而Vitest可以直接使用,无需任何配置。这是我的个人经验。我花了几天时间让Jest“勉强”工作。而我在安装后立即让Vitest工作。

我不想抨击Jest,实际上我认为它是一个很棒、直观的测试工具。但最终Vitest“只是工作了”,而且你可以使用我们都喜欢的同样简单的Jest风格API。

步骤(我使用pnpm,但当然你也可以使用yarn或npm):

pnpm remove jest ts-jest @types/jest
pnpm add -D vite vitest

删除 jest.config.js 文件,并创建 vite.config.ts 文件:
/// <reference types="vitest" />

import { defineConfig } from 'vite'

export default defineConfig({
  test: {
    /* for example, use global to avoid globals imports (describe, test, expect): */
    // globals: true,
  },
})

添加到你的测试中:

import { assert, expect, test } from 'vitest'

更新你的 package.json:

"test": "vitest",

1
我放弃了与 Jest 的战斗,转而使用只需 npm i vitest 而无需任何配置的 Vitest。 - Maksim Shamihulau
百分之百同意。我对Vitest的快速和简单运行感到非常高兴。 - undefined

8

由于Jest不能很好地与ES Modules一起工作,因此您需要在jest.config.js中添加这些配置,以告诉Jest改为使用CommonJS构建。

moduleNameMapper: {
  '^variables$': 'variables/dist/cjs',
  '^[NAME OF MODULE YOU WANT TO IMPORT]$': '[NAME OF MODULE YOU WANT TO IMPORT]/dist/cjs'
}

4
你能解释一下你的解决方案吗?或者给出关于moduleNameMapper的文档链接吗? - Dmitry Malugin
1
你有一个模块的例子吗?而不是模块的名称?(我遇到问题的模块没有dist/cjs文件夹。所以即使我添加了“^ol$”:“ol/dist/ol.js”,我仍然会收到错误提示。) - Peter

5
您不一定需要将babel-jest转换为ts-jest。
相反:
1. 将您的.babelrc重命名为babel.config.json,详情请参见https://babeljs.io/docs/en/configuration#whats-your-use-case 2. 添加transformIgnorePatterns:
    "transformIgnorePatterns": [
      "node_modules/(?!variables/.*)"
    ]

这个方法解决了类似的问题,而无需添加额外的转换模式。 .babelrc 是项目本地的,因此不会应用于 Jest 中的 node_modules


谢谢!那个方法可行! 我有一个第三方依赖项(lodash-es),并且已经使用默认配置的 babel 配置了 jest。 但是 jest 在 lodash-es 中触发了一个错误,所以添加 transformIgnorePatterns: ['node_modules/(?!lodash-es/.*)'] 强制 lodash 进行转译,现在测试可以正常工作了!注意:我在代码和测试中都使用 TypeScript 的 ESM 导入,并且仅在 precommit hook 上运行 tsc,因此不需要完全配置 jest 与 TypeScript(或 ts-jest)。 - Виталий Абрамов
当你说'.babelrc是针对你的项目本地的,所以它不会应用于Jest中的node_modules'时,你能详细说明一下吗?是否有任何描述这个问题的文档? - blu10

4

这对我有用。

npm i ts-jest -D

在您的根目录中创建jest.config.js

然后将以下内容添加到其中:

module.exports = {
  "roots": [
    "<rootDir>/src"
  ],
  "testMatch": [
    "**/__tests__/**/*.+(ts|tsx|js)",
    "**/?(*.)+(spec|test).+(ts|tsx|js)"
  ],
  "transform": {
    "^.+\\.(ts|tsx)$": "ts-jest"
  },
}

4

我偶然发现了一种方法,让预处理器可以处理整个node_modules文件夹,而这通常是被禁止的:

默认情况下,转换器会忽略“node_modules”文件夹。

transformIgnorePatterns:
[
    'node_modules'
]

使用这种模式似乎可以绕过Jest的内部检查。

transformIgnorePatterns:
[
    '//node_modules'
]

这是一个黑客行为吗?是的!速度慢吗?是的!但是,如果正确使用,它可以帮助需要快速修复的人。


3

对于由antd框架引起的此类问题。

背景

您将antd DatePicker重写为使用dayjs而不是默认的momentjs。

您按照这里的说明进行操作。

现在,如果您运行测试,它将产生那个可怕的错误。

SyntaxError: Cannot use import statement outside a module

这是因为这个原因。
import generatePicker from "antd/es/date-picker/generatePicker";
import "antd/es/date-picker/style/index";

这个问题是由于从 antd/es/ 导入引起的。

解决方法是改为从 antd/lib/ 导入。

import generatePicker from "antd/lib/date-picker/generatePicker";
import "antd/lib/date-picker/style/index";

这修复了测试错误,但是另一个问题出现了,这段代码有一个bug。 DatePicker 组件现在将不会遵守本地化设置。

因此,最终的解决方案是添加:

  moduleNameMapper: {
    ... other mappings here ...
    
    // Add this to fix the problem!
    "^antd/es/(.*)$": "antd/lib/$1",
  }

在你的jest.config.js文件中

这将允许你在你的代码中使用antd/es/,但会在测试中替换为antd/lib/

参考这里

希望这能帮助任何人,我花了一些时间才弄明白。


3
当我尝试测试我的应用程序并且某些功能使用了文件类型库时,我遇到了相同的问题。
我在我的package.json中进行了如下的jest配置。
"jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "jsx",
      "node",
      "mjs"
    ],
    "transformIgnorePatterns": []
  },

我认为重要的是只使用"transformIgnorePatterns": []。
transformIgnorePatterns设置为空数组意味着Jest将使用指定的转换器(在本例中为babel-jest)来转换每个导入的模块。默认情况下,Jest不会转换node_modules中的任何内容,这可能会导致当一个模块(或其依赖项)使用ES6导入时出现问题。
如果您只在某些模块中遇到此问题,您可以进一步细化transformIgnorePatterns以仅排除那些特定的模块不被忽略,但如果空数组能很好地工作并且不引入其他复杂性,您可以坚持使用它。

它能够工作。但是它的速度非常慢,因为它要转换整个node_modules文件夹,而在某些项目中,这个文件夹可能非常大。 - undefined
我在使用jest进行自动化测试时遇到了相同的问题。无法导入skia-canvas的Index.node文件,现在已经解决了。 - undefined

2

我通过在.babelrc -> presets -> @babel/preset-env中添加"modules": "commonjs"来解决我的问题。

.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": "commonjs"
      }
    ],
    "@babel/preset-react"
  ]
}

package.json

"jest": {
  "verbose": true,
  "moduleDirectories": ["node_modules", "src"],
  "testEnvironment": "node",
  "transformIgnorePatterns": [
    "node_modules/(?!variables/.*)"
  ],
  "transform": {
    "^.+\\.jsx?$": "babel-jest"
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接