javascript - Next.js och Jest: SyntaxError: Kan inte använda import-satsen utanför en modul
Jag arbetar på ett Next.js-projekt med TypeScript och för testning använder jag Jest och React Testing Lib. Men jag stöter på ett SyntaxError: Cannot use import statement outside a module för komponenter där jag importerar rehype-raw.
Såvitt jag förstår detta, stödjer Jest inte ES6, så node_modules kan behöva transformeras. Detta kan konfigureras med transformIgnorePatterns. Till exempel, om rehype-raw orsakar detta fel, bör användning av "transformIgnorePatterns": ["node_modules/(?!rehype-raw)/"] tillåta transformation av rehype-raw men inget annat modul. Och på så sätt lösa detta fel.
Men detta fungerar inte för mig. Men jag vet inte varför och hur jag kan lösa detta. Ingen föreslagen lösning jag har hittat kunde lösa detta problem. Jag har bifogat min felutdata, jest.config.js och babel.rc-fil nedan.
Felfunktion
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"]
}
Svar #1
Jest stöder inte ECMAScript-moduler vilket är vad hast-util-raw använder. Andra problemet; transformIgnorePatterns fungerade inte heller så nedan är lösningen för mig själv med en Next.JS-setup.
1. Ta bort babel.rc
Radera din babel.rc. Du kan bara använda ändringarna nedan.
2. Lägg till moduleNameMapper
Detta åtgärdar ett annat fel där parse5 inte kan hittas. Krävs av 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. Lägg till transformIgnorePatterns
Lägg till transformIgnorePatterns dock precis i slutet. Som jag sa, detta fungerade inte att lägga direkt i konfigurationen av någon anledning. Jag var också tvungen att lägga till varje paket som används av hast-util-raw. Jag är säker på att det finns ett bättre sätt att göra detta på dock :)
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)/)'
]
})
Min fullständiga konfiguration om någon är intresserad...
/* 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)/)'
]
})
Svar #2
Det du behöver förstå är att Next.js kommer att säga till Jest att inte transformera paket under /node_modules förutom de som definieras i deras konfigurationsfil (egenskapen transpilePackages).
Det är lite av ett kaos att lägga till korrekt transformeringsundantag över detta, men https://github.com/vercel/next.js/discussions/31152#discussioncomment-1697047 satte mig på rätt spår.
Om du inte vill hantera arrayen manuellt eftersom den beror på transpilePackages på Next.js-sidan, återanvände jag deras logik på https://github.com/vercel/next.js/blob/435eca3fc5b5dd23cad6cff43632bdcc777727d9/packages/next/src/build/jest/jest.ts#L156-L173 för att transformera både de från transpilePackages men också de som behövs för att Jest ska fungera korrekt.
Här är skillnaden i min lösning: https://github.com/inclusion-numerique/mediature/commit/cfe3ad85ab13d3f38d863afb8ee24b6995ae4e00
Jag ber om ursäkt för att inte lägga direkt kod på detta inlägg, det skulle göra saker oklara.
