skip to Main Content

I am working on a Next.js project using TypeScript and for testing I use Jest and React Testing Lib. However, I encounter a SyntaxError: Cannot use import statement outside a module for components where I import rehype-raw.

As far as I understand this, Jest does not support ES6 so node_modules may need to be transformed. This can be configured using transformIgnorePatterns. For example if rehype-raw is causing this error using "transformIgnorePatterns": ["node_modules/(?!rehype-raw)/"] should allow transformation of the rehype-raw but no other module. And thus solve this error.

However, this does not work for me. But idk why and how I can solve this. No suggested solution I have found could solve this problem. I have attached my error output, jest.config.js and babel.rc file below.

Error output

 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"]
}

4

Answers


  1. Did you already use type:"module" in package.json?

    Login or Signup to reply.
  2. Next has already out-of-the-box support for Jest, I’d suggest you follow the steps provided in the docs.

    Login or Signup to reply.
  3. Jest does not support ECMAScript Modules which is what hast-util-raw uses. Second problem; transformIgnorePatterns did not work too so below is the fix for myself with a Next.JS setup.

    1. Delete babel.rc

    Delete your babel.rc. You can just use the below changes.

    2. Add moduleNameMapper

    This fixes a different error where parse5 can not be found. Required by 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. Add transformIgnorePatterns

    Add transformIgnorePatterns however right at the end. As I said, this did not work adding directly into the config for som reason. I also had to add every package used by hast-util-raw. I am sure there is a better way to do this though 🙂

    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)/)'
      ]
    })
    
    

    My full config if anyone is interested…

    /* 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)/)'
      ]
    })
    
    
    Login or Signup to reply.
  4. What you need to understand is Next.js will tell Jest to not transform packages under /node_modules except those defined in their config file (the transpilePackages property).

    It’s a bit of a mess to add properly transform exclusions over this, but https://github.com/vercel/next.js/discussions/31152#discussioncomment-1697047 put me on the right track.

    If you don’t want to manually manage the array since it depends on transpilePackages on the Next.js side, I reused their logic at https://github.com/vercel/next.js/blob/435eca3fc5b5dd23cad6cff43632bdcc777727d9/packages/next/src/build/jest/jest.ts#L156-L173 to transform both those from transpilePackages but also the ones needed for Jest to run properly.

    Here the diff of my workaround:
    https://github.com/inclusion-numerique/mediature/commit/cfe3ad85ab13d3f38d863afb8ee24b6995ae4e00

    I’m sorry to not put direct code on this post, it would make things unclear.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search