Jest 不会转换模块 - 语法错误:无法在模块外部使用导入语句



无论我尝试什么,我都无法摆脱这个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']

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

  • 在开玩笑配置中,testPathIgnorePatternstransformIgnorePatterns

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

。等等。

我有这个设置:

包.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()

变量.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')
})
})

关于如何解决此问题的任何想法?

即使我单独尝试过它们,我也没有一起尝试过它们(transformtransformIgnorePatterns)。所以这个开玩笑的配置解决了我的问题:

"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(我想当开玩笑的预设定义为ts-jest时,这是一个问题。因为如果我将其更改为babel-jest它会再次抛出相同的错误。
--- "node_modules/variables/.+\.(j|t)sx?$": "babel-jest"
+++ "node_modules/variables/.+\.(j|t)sx?$": "ts-jest"

我的问题有所不同,jest会偶然发现.js文件来自node_modulesSyntaxError: 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配置

由于 Jest 不能很好地处理 esmodules,因此您需要在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'
}

你不一定需要将转换器从 babel-jest 更改为 ts-jest。

相反:

  1. .babelrc重命名为babel.config.jsonhttps://babeljs.io/docs/en/configuration#whats-your-use-case

  2. 添加转换忽略模式:

"transformIgnorePatterns": [
"node_modules/(?!variables/.*)"
]

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

偶然地,我发现了一个黑客,让pre-processor处理通常禁止的整个node_modules

默认情况下,变压器会忽略"node_modules"文件夹。

transformIgnorePatterns:
[
'node_modules'
]

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

transformIgnorePatterns:
[
'//node_modules'
]

这是黑客吗?是的!这很慢吗?是的!但正确地,它可以帮助需要快速修复的人。

对于由于antd框架而遭受这种痛苦的人。

场景

你覆盖antd DatePicker以使用day js而不是默认的时刻js。

您按照此处的说明操作

现在,如果您运行测试,它将出现可怕的错误

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/es/导入引起的

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

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

这修复了测试错误,但是,会出现的另一个问题是此代码存在错误。日期选取器组件现在将不遵循区域设置。

所以最终的解决方案是添加

moduleNameMapper: {
... other mappings here ...

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

在你的jest.config.js文件中

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

参考这里

希望这对任何人都有帮助,这花了我一段时间才弄清楚。

使用 Vitest。

我已经尝试了这里提出的大多数建议。我发现 Jest 正确安装非常痛苦,而 Vitest 无需任何配置即可开箱即用。这是我的个人经历。我花了好几天才让杰斯特开始工作。安装后,我让 Vitest 立即工作。

我不想讨厌Jest,我实际上认为这是一个很棒且直观的测试工具。但最终Vitest"Just Works"(tm),你可以使用我们都喜欢的同样简单的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'

更新您的包.json:

"test": "vitest",

解决它的另一种方法是以及可能的其他有关 babel 和打字稿的后续问题是使用ts-jest,引用自 Jest 的入门

但是,将 TypeScript 与 Babel 一起使用有一些注意事项。因为 Babel 中的 TypeScript 支持纯粹是转译,Jest 不会在测试运行时对其进行类型检查。如果你想要这样,你可以改用ts-jest,或者单独运行TypeScript编译器tsc(或作为构建过程的一部分)。

我陷入了同样的情况。由于我有一个基于 TypeScript 的私有未翻译包,并且我所有的源文件和测试文件都使用 ECMA(导入语法)应用,我也遇到了以下错误。

语法错误:不能在模块外部使用导入语句

我尝试过的解决方案。

  • 以上答案,如在jest.config.ts中使用transform,transformIgnorePatterns,moduleNameMapper
  • 遵循JEST官方文档,Configuration Jest#transformignorepatterns-arraystring,我使用了完全相同的方法,甚至参考了React Native Guide中的用例。
  • 删除了所有node_modules,包括缓存,然后重新安装它们。
  • 使用反应脚本并将笑话通天塔从 27 降级到 26。还发生了另一个问题。

毕竟,我从 JEST 官方文档中找到了 ECMAScript 模块,这四个步骤完美地解决了我的问题。我在这里粘贴了他们的说明,但是,您应该查看文档本身。

  1. 确保通过传递 transform: {} 来禁用代码转换,或者将转换器配置为发出 ESM 而不是默认的 CommonJS (CJS)。
  2. 使用 --experimental-vm-modules 执行节点,例如 node --experimental-vm-modules
  3. node_modules/.bin/jest 或 NODE_OPTIONS=--experimental-vm-modules npx jest 等。在Windows上,您可以使用cross-env来设置环境变量。
  4. 除此之外,我们尝试遵循节点的逻辑来激活"ESM模式"(例如查看package.json或mjs文件中的类型),有关详细信息,请参阅其文档。
  5. 如果要将其他文件扩展名(如ts)视为ESM,请使用扩展名ToTreatAsEsm选项。

通过在.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"
}
}

这对我有用。

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"
},
}
<</div> div class="one_answers">

tl;dr: tsconfig.spec.json>{ "compilerOptions": { "allowJs": true } }

这里提到的其他所有内容都做了:

  • 转换忽略模式
  • 向转换部分添加其他模式
  • 。之后我恢复了更多

然后我调试到变压器中。首先想想我意识到:预配置的工作区不会记录任何内容,甚至不会记录到 ts-jest.log,没有--verbose--debug,甚至没有警告,那将是:

有一个.js文件要编译allowJs而选项未设置为true(文件:{{path}})。要解决此问题,请执行以下操作:

  • 如果您希望 TypeScript 处理 JS 文件,请在 TypeScript 配置(通常为 tsconfig.json)中将allowJs设置为true
  • 如果您不希望 TypeScript 处理您的.js文件,请在 Jest 配置中更改transform键,该值ts-jest,使其不再.js文件匹配

(参见:ts-jest-transformer.ts)

一个简单的解决方案是在jest.config.js文件中使用不同的预设。

尝试使用"preset": "ts-jest/presets/js-with-ts"等预设,而不是使用默认"preset": "ts-jest"。根据文档,它将:

ts-jest/

presets/js-with-ts

TypeScript 和 JavaScript 文件(.ts、.tsx、.js、.jsx)将由 ts-jest 转换为 CommonJS 语法。 您需要在 tsconfig.json 文件中将 allowJs 设置为 true。

我解决了我的问题,模拟了文件 例如: jest.mock('rehype-raw/index.js', () => jest.fn());

这在测试文件的顶部

我在这里尝试了很多答案,但没有任何效果。我正要放弃jest但找到了解决方案。我将把这些步骤记录为清单。

首先,确保已安装jest

npm install --save-dev jest

其次,在package.json文件中启用 ESM:

{
...
"type": "module"
}

第三,更新package.json文件中的test属性:

"scripts": {
"test": "node --experimental-vm-modules ./node_modules/.bin/jest"
}

之后,创建jest.config.js并添加以下内容:

export default { transform: {} };

有了这个,再次开玩笑:

npm test

如果使用直接路径:

现在 ts-jest 版本 28 和 29 坏了。我不知道它影响的所有版本,但它至少会影响28.1.3-29.1.0.它记录在这里:https://github.com/nodkz/mongodb-memory-server/issues/679

错误在于,指定moduleDirectories: ["node_modules", "src"]以意想不到的方式打破了玩笑,包括本质上对你的进出口超级生气。解决方法是将行更改为moduleDirectories: ["node_modules", "<rootdir>/src/"]或完全删除它。注意:这些解决方案不允许原始功能

可能包含此行的原因是允许直接路径,因此您可以编写import { minute } from "utils";而不是import { minute } from "../../../utils";在我看来,这非常方便。上述修复程序将允许您进行编译,但您可能仍然会收到类似cannot find module "utils" in /some/directory/path

我能够找到的唯一修复程序是使用moduleNameMapper选项,如下所示:

"moduleNameMapper": {
'^utils(.*)$': '<rootDir>/src/utils$1',
},

信用回答 https://stackoverflow.com/a/67667249

这意味着您现在必须显式指定将使用绝对导入引用哪些文件。虽然不理想,但我无法让其他任何东西工作

作为参考,这是我完整的jest.config.js文件:

/* eslint-disable import/no-commonjs */
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest/presets/js-with-ts",
testEnvironment: "node",
roots: ["<rootDir>/src"],
"transformIgnorePatterns": [
"node_modules/(?!variables/.*)"
],
testPathIgnorePatterns: ["<rootDir>/dist/", "node_modules"],
moduleDirectories: ["node_modules", "<rootdir>/src/"],
setupFiles: ['dotenv/config'],
collectCoverage: true,
coverageDirectory: "coverage",
extensionsToTreatAsEsm: ['.ts'],
"moduleNameMapper": {
"/src/(.*)": "<rootDir>/src/$1",
'^utils(.*)$': '<rootDir>/src/utils$1',
},
"transform": {
"node_modules/variables/.+\.(j|t)sx?$": "ts-jest"
},
};

对于您的tsconfig.json,您可能需要考虑以下设置:

"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"moduleResolution": "Node",
"module": "ESNext",
"target": "ES2020",
``

我在以下文档安装 NestJS 时遇到了同样的问题:https://docs.nestjs.com/first-steps。

修复是在"jest"配置下的package.json文件中添加缺少"preset": "ts-jest",

"jest": {
...
"preset": "ts-jest",
...
}

当我尝试测试我的应用程序并且某些函数正在使用文件类型库时,我遇到了同样的问题。

我在我的包中做了如下开玩笑的配置.json

"jest": {
"moduleFileExtensions": [
"js",
"json",
"jsx",
"node",
"mjs"
],
"transformIgnorePatterns": []
},

我认为重要的是只使用"transformIgnorePatterns":[]。

transformIgnorePatterns设置为空数组意味着 Jest 将使用指定的转换器(在本例中为babel-jest)转换每个导入的模块。默认情况下,Jest 不会在 node_modules 中转换任何内容,这可能会导致模块(或其依赖项)使用 ES6 导入时出现问题。

如果您只在模块子集上遇到此问题,则可以进一步优化transformIgnorePatterns以仅排除那些特定的模块,但如果空数组运行良好并且不会引入其他复杂性,则可以坚持使用。

最新更新