Getting jest to respect transformIgnorePatterns inside .bs.js file

I have been trying to get my tests to run with jest, after I switched from CRA to nextjs. I’ve been having a hell of a time.

I get this error:

import * as Curry from "rescript/lib/es6/curry.js";
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

       6 |
       7 | // @ts-ignore: Implicit any on import
    >  8 | import * as CategorySelectButtonBS__Es6Import from './CategorySelectButton.bs';
         | ^
       9 | const CategorySelectButtonBS: any = CategorySelectButtonBS__Es6Import;
      10 |
      11 | // tslint:disable-next-line:interface-over-type-literal

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
      at Object.<anonymous> (src/rescript/components/CategorySelectButton.gen.tsx:8:1)

This makes me think I needed to make sure this was in my transformIgnorePatterns for jest. But in reality, it’s already there. Here’s my jest.config.js:

module.exports = {
  testEnvironment: "jsdom",
  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
  testPathIgnorePatterns: ["<rootDir>/.next/", "<rootDir>/node_modules/"],
  setupFiles: ["dotenv/config"],
  moduleNameMapper: {
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
      "<rootDir>/__mocks__/fileMock",
    "\\.(css|less)$": "<rootDir>/jest/__mocks__/styleMock",
  },
  transformIgnorePatterns: [
    "/node_modules/(?!(@apollo|cropperjs|react|rescript)/)",
  ],
  preset: "ts-jest",
  transform: {
    "^.+\\.(ts|tsx)?$": "ts-jest",
    "^.+\\.(ts|jsx)$": "babel-jest",
  },
  globals: {
    // This is necessary because next.js forces { "jsx": "preserve" }, but ts-jest appears to require { "jsx": "react" }
    'ts-jest': {
      tsconfig: {
        jsx: 'react',
      },
    },
  }
};

My bsconfig.json:

{
  "name": "__",
  "namespace": false,
  "reason": {
    "react-jsx": 3
  },
  "bs-dependencies": [
    "@reasonml-community/graphql-ppx",
    "@rescript/react",
    "rescript-apollo-client",
    "rescript-material-ui",
    "rescript-webapi"
  ],
  "ppx-flags": [
    "@reasonml-community/graphql-ppx/ppx"
  ],
  "graphql": {
    "objects": true,
    "apolloMode": true,
    "extendMutation": "ApolloClient.GraphQL_PPX.ExtendMutation",
    "extendQuery": "ApolloClient.GraphQL_PPX.ExtendQuery",
    "extendSubscription": "ApolloClient.GraphQL_PPX.ExtendSubscription",
    "templateTagReturnType": "ApolloClient.GraphQL_PPX.templateTagReturnType",
    "templateTagImport": "gql",
    "templateTagLocation": "@apollo/client"
  },
  "gentypeconfig": {
    "language": "typescript",
    "module": "es6",
    "importPath": "relative",
    "shims": {
      "Js": "Js"
    },
    "debug": {
      "all": false
    },
    "exportInterfaces": false
  },
  "sources": [
    {
      "dir": "src",
      "subdirs": true
    }
  ],
  "package-specs": {
    "module": "es6",
    "in-source": true
  },
  "suffix": ".bs.js",
  "warnings": {
    "number": "-3",
    "error": "+101+8"
  }
}

And not sure if it matters, but here is my tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "noImplicitAny": true,
    "typeRoots": ["./node_modules/@types", "./types/"],
    "noFallthroughCasesInSwitch": true
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "src"],
  "exclude": ["node_modules", "**/*.spec.ts"],
  "plugins": [
    {
      "name": "typescript-tslint-plugin"
    }
  ]
}

This is a .tsx test that is rendering a component accessing a .gen.tsx file that is accessing a .bs.js file where the transform ignore is not really working. Anyone have any ideas?

Do you have type:module in package.json?

I didn’t but I did change it to that just now. That required my jest.config.js to use export default instead of module.exports = which I had read solved it for someone else. But it didn’t solve it for me just now :frowning: package.json is now set to type: "module" but it’s still failing.

Not sure if it matters, but this is my babel.config.json. I’m pretty sure it shouldn’t matter for this… since these files are built with bsconfig.json.

//babel.config.json
{
  "presets": ["next/babel", "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
  "plugins": []
}

What’s the error after this?

I still had the exact same error about using import outside a module.

I did make small progress. I added experimental modules flag, even though I’m on node v14, and got a different error:


(node:830) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
 FAIL  src/components/giftees/GifteeList.test.tsx
  ● Test suite failed to run

    Must use import to load ES Module: /home/user/Repos/App/realm-app/hosting/src/rescript/components/GiftCategorySelectButton.bs.js

       6 |
       7 | // @ts-ignore: Implicit any on import
    >  8 | import * as GiftCategorySelectButtonBS__Es6Import from './GiftCategorySelectButton.bs';
         | ^
       9 | const GiftCategorySelectButtonBS: any = GiftCategorySelectButtonBS__Es6Import;
      10 |
      11 | // tslint:disable-next-line:interface-over-type-literal

      at Runtime.requireModule (node_modules/jest-runtime/build/index.js:972:21)
      at Object.<anonymous> (src/rescript/components/GiftCategorySelectButton.gen.tsx:8:1)
      at Object.<anonymous> (src/components/gifts/InterestSelect.tsx:11:1)

It is using import so now I just don’t know why it’s complaining -_-

It must have .js extension at the end.

You mean like ./GiftCategorySelectButton.bs.js? I just tried that but it did not work either.

1 Like

I’ve still been struggling with this. I managed to get it to load the GiftCategorySelectButton, but now it complains about ES Module inside of that:

 Must use import to load ES Module: /home/luksd/Repos/Occasionally/realm-app/hosting/node_modules/rescript/lib/es6/curry.js

      4 | import * as React from "react";
      5 | import * as Core from "@material-ui/core";
    > 6 | import * as Styles from "@material-ui/styles";
        |                                     ^
      7 |
      8 | var useStyles = Styles.makeStyles(function (theme) {
      9 |       return {

      at Runtime.requireModule (node_modules/jest-runtime/build/index.js:924:21

It’s worth mentioning I use nextjs, and I’ve changed my jest.config.js quite a bit:

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: './',
})

const customJestConfig = {
  testEnvironment: "jsdom",
  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
  testPathIgnorePatterns: ["<rootDir>/.next/", "<rootDir>/node_modules/"],
  setupFiles: ["dotenv/config"],
  moduleNameMapper: {
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
      "<rootDir>/__mocks__/fileMock",
    "\\.(css|less)$": "<rootDir>/jest/__mocks__/styleMock",
    "^uuid$": "uuid"
  },
  transformIgnorePatterns: ["/node_modules/(?!(@apollo|cropperjs|react|rescript|uuid)/)", '/node_modules/'],
  preset: "ts-jest",
  transform: {
    "^.+\\.(ts|tsx)?$": ["ts-jest", { "tsConfig": { jsx: "react" } }],
    "^.+\\.(js|jsx)$": "babel-jest",
  },
};

module.exports = createJestConfig(customJestConfig)

this has gotten me pretty close, but I still can’t get this dang test to run because of ESM errors… but I feel like ESM is not really the problem. Any ideas?

Does it work when you run next application?

Yes it works. Next builds this stuff fine, just jest troubles.

That said, now I finally got this test to work by adding this to moduleNameMapper in jest.config.js:

"rescript/lib/es6/(.*)": "<rootDir>/node_modules/rescript/lib/js/$1"

I am on the final error it seems, and again I cant seem to get a transformIgnorePattern to work on rescript-apollo-client:

/home/luksd/Repos/Occasionally/realm-app/hosting/node_modules/rescript-apollo-client/src/@apollo/client/react/hooks/ApolloClient__React_Hooks_UseQuery.bs.js:3
    import * as Curry from "rescript/lib/es6/curry.js";
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

       5 | import * as ApolloClient__React_Hooks_UseMutation from "rescript-apollo-client/src/@apollo/client/react/hooks/ApolloClient__React_Hooks_UseMutation.bs.js";
       6 |
    >  7 | var Raw = {};
         |                                                      ^
       8 |
       9 | var query = (require("@apollo/client").gql`
      10 |   query GetGiftProfileQuery($query: GiftProfileQueryInput)  {

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
      at Object.<anonymous> (src/rescript/graphql/queries/GQL__GiftProfile.bs.js:7:66)

And yet my transformIgnorePatterns looks like this:
"/node_modules/(?!(@apollo|cropperjs|react|rescript|rescript-apollo-client)/)" and I even tried this /node_modules/(?!(rescript-apollo-client)).+\\.bs\\.js$

I noticed inside rescript-apollo-client there is only es modules, where as for the rescript library I could map it to commonjs folders. However, I am not sure why jest needed the modulenameMapper, with my transformIgnorePattern. It leads me to believe this transformIgnorePattern does not work very well inside my .bs.js files. Any ideas?