Next.js和Jest:SyntaxError:不能在模块外使用import语句



我正在使用TypeScript进行Next.js项目,为了进行测试,我使用了Jest和React testing Lib。但是,我遇到一个SyntaxError:对于我导入的组件,不能在模块外使用import语句。

据我所知,Jest不支持ES6,因此可能需要转换node_modules。这可以使用transformIgnorePatterns进行配置。例如,如果rehype-raw导致此错误,则使用"transformIgnorePatterns": ["node_modules/(?!rehype-raw)/"]应允许rehype-raw的转换,但不允许其他模块的转换。从而解决了这个错误。

然而,这对我不起作用。但我想知道为什么以及如何解决这个问题。我没有找到任何建议的解决方案可以解决这个问题。我在下面附上了我的错误输出jest.config.jsbabel.rc文件。

错误输出

FAIL  test/pages/companies/[id].test.tsx                  
● Test suite failed to run
Jest encountered an unexpected token
[...]
Details:
/path/frontend-job/node_modules/rehype-raw/index.js:7
import {raw} from 'hast-util-raw'
^^^^^^
SyntaxError: Cannot use import statement outside a module
3 | import Image from 'next/image';
4 | import { Remark } from 'react-remark';
> 5 | import rehypeRaw from 'rehype-raw';
| ^
6 | import rehypeSanitize from 'rehype-sanitize';
7 | import { logError } from '@utils/logger';
8 |
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
at Object.<anonymous> (components/markdown/JobMarkdown.tsx:5:1)

jest.config.js

const { resolve } = require('path');
module.exports = {
roots: ['<rootDir>'],
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'],
setupFiles: ['<rootDir>/test/setup.js'],
testPathIgnorePatterns: ['<rootDir>[/\\](node_modules|.next)[/\\]'],
transform: {
'^.+\.(ts|tsx)$': 'babel-jest',
},
transformIgnorePatterns: [
'node_modules/(?!rehype-raw)/',
],
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
moduleNameMapper: {
// Force mocks: https://github.com/facebook/jest/issues/4262
'@api/axios': '<rootDir>/test/__mocks__/axios.js',
// Normal module aliases
'\.(css|less|sass|scss)$': 'identity-obj-proxy',
'\.(gif|ttf|eot|svg|png)$': '<rootDir>/test/__mocks__/fileMock.js',
'^@test/(.*)$': resolve(__dirname, './test/$1'),
'^@test/faker/(.*)$': resolve(__dirname, './test/faker/$1'),
'^@components/(.*)$': resolve(__dirname, './components/$1'),
'^@pages/(.*)$': resolve(__dirname, './pages/$1'),
'^@utils/(.*)$': resolve(__dirname, './utils/$1'),
'^@api/(.*)$': resolve(__dirname, './api/$1'),
'^@store/(.*)$': resolve(__dirname, './store/$1'),
},
testEnvironment: 'jsdom',
};

babel.rc

{
"presets": ["next/babel"]
}

Jest不支持hast-util-raw使用的ECMAScript模块。第二个问题;transformIgnorePatterns也不起作用,所以下面是我使用Next.JS设置的修复方法。

1.删除babel.rc

删除您的babel.rc。您可以使用以下更改。

2.添加moduleNameMapper

这修复了找不到parse5的另一个错误。hast-util-raw要求

/** @type {import('jest').Config} */
const customJestConfig = {
//...
moduleNameMapper: {
'parse5/lib/parser/index.js':
'<rootDir>/node_modules/hast-util-raw/node_modules/parse5/lib/parser/index.js',
}
}

3.添加transformIgnorePatterns

在末尾添加transformIgnorePatterns,但向右。正如我所说,由于某种原因,这无法直接添加到配置中。我还必须添加hast-util-raw使用的每个包。不过,我相信有更好的方法可以做到这一点:(

module.exports = async () => ({
...(await createJestConfig(customJestConfig)()),
transformIgnorePatterns: [
'node_modules/(?!(rehype-raw|hast-util-raw|unist-util-position|unist-util-visit|unist-util-visit-parents|unist-util-is|hast-util-from-parse5|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)/)'
]
})

如果有人感兴趣,我的完整配置。。。

/* eslint-disable @typescript-eslint/no-var-requires */
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './'
})
// Add any custom config to be passed to Jest
/** @type {import('jest').Config} */
const customJestConfig = {
// Add more setup options before each test is run
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
// if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
moduleDirectories: ['<rootDir>/node_modules', '<rootDir>/src', '<rootDir>/src/pages'],
testEnvironment: 'jest-environment-jsdom',
testPathIgnorePatterns: [
'<rootDir>/.next/',
'<rootDir>/node_modules/',
'<rootDir>/coverage',
'<rootDir>/dist'
],
moduleNameMapper: {
'react-markdown': '<rootDir>/node_modules/react-markdown/react-markdown.min.js',
'parse5/lib/parser/index.js':
'<rootDir>/node_modules/hast-util-raw/node_modules/parse5/lib/parser/index.js'
},
resolver: '<rootDir>/.jest/resolver.js',
clearMocks: true,
testTimeout: 20000
}
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
// module.exports = createJestConfig(customJestConfig)
module.exports = async () => ({
...(await createJestConfig(customJestConfig)()),
transformIgnorePatterns: [
'node_modules/(?!(rehype-raw|hast-util-raw|unist-util-position|unist-util-visit|unist-util-visit-parents|unist-util-is|hast-util-from-parse5|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|vfile-location|web-namespaces|hast-util-to-parse5|zwitch|html-void-elements)/)'
]
})

您需要了解的是,Next.js将告诉Jest不要转换/node_modules下的包,除非它们的配置文件(transpilePackages属性(中定义了那些包。

在上面添加适当的转换排除有点混乱,但是https://github.com/vercel/next.js/discussions/31152#discussioncomment-1697047让我走上了正轨。

如果您不想手动管理阵列,因为它依赖于Next.js端的transpilePackages,我在https://github.com/vercel/next.js/blob/435eca3fc5b5dd23cad6cff43632bdcc777727d9/packages/next/src/build/jest/jest.ts#L156-L173来转换来自CCD_ 15的那些以及Jest正确运行所需的那些。

以下是我的变通方法:https://github.com/inclusion-numerique/mediature/commit/cfe3ad85ab13d3f38d863afb8ee24b6995ae4e00

很抱歉没有在这篇文章中直接添加代码,这会让事情变得不清楚

Next已经开箱即用地支持Jest,我建议您按照文档中提供的步骤操作。

您是否已经使用了类型:"模块";在package.json中?

最新更新