React-markdown + typescript + jest

Hi redwood community!

I am trying to use react-markdown in a new redwood 4.3 project with typescript. When I try running tests for any component that uses react-markdown I get some issues here

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /Users/sglyon/src/jupyteach/jupyteach-rw/node_modules/react-markdown/index.js:6
    export {uriTransformer} from './lib/uri-transformer.js'
    ^^^^^^
    SyntaxError: Unexpected token 'export'

    > 1 | import ReactMarkdown from 'react-markdown'
        | ^
      2 | import rehypeKatex from 'rehype-katex'
      3 | import remarkMath from 'remark-math'
      4 | import 'katex/dist/katex.min.css'

The Markdown component is pretty straightforward and just looks like this:

import ReactMarkdown from 'react-markdown'
import rehypeKatex from 'rehype-katex'
import remarkMath from 'remark-math'
import 'katex/dist/katex.min.css'

const Markdown = ({ content }: { content: string }) => {
  return (
    <div className="prose max-w-none">
      <ReactMarkdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>
        {content}
      </ReactMarkdown>
    </div>
  )
}

export default Markdown

Any suggestions on how to get this resolved? I’ve googled around and tried various things for a few hours with no luck :frowning:

2 Likes

any luck solve this problem yet?

The issue relates to jest not being able to transform ES Modules properly.

I can get some parts to work if I specify the modules in my jest.config.js. transformIgnorePatterns: ['node_modules/(?!(@thi.ng)/)'], (Seems like it has to be the web jest.config.js, since I use that module on web side)

And within my package.json (root) I use a script:
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules && yarn rw test web"
With node 18 this one should work too:
"test": "cross-env NODE_OPTIONS=--input-type=module && yarn rw test web"

But I run into the next issue: Cannot use 'import.meta' outside a module This relates again to missing ES Module support of jest. It only happens in libraries that are ESM only and in case of import.meta, already use vite. A solution mentioned here, did not help: [Bug]: SyntaxError: Cannot use 'import.meta' outside a module · Issue #12183 · facebook/jest · GitHub

Vitest wouldn´t bother at all with this, would add speed and a nice dashboard. @danny I know you have been busy with Vite setup and might not like looking into vitest, but I think its worth a try? Since vitest and jest are pretty similar, the switch normally is rather easy.

1 Like

Hi @dennemark! I’m honestly no very familiar with Vitest, but if you are keen we would be happy to receive a PR so we can evaluate it’s viability!

Let me know if I can help in anyway :slight_smile:

The solution to this problem is probably to downgrade to a version of react-markdown that isn’t ESM only!

@danny do you have any recommendations where to look at?
I hope it is enough to look into packages/cli /cli-helpers /testing as well as create-redwood-app/template configs

I hope it is enough to look into packages/cli /cli-helpers /testing as well as create-redwood-app/template configs

Sounds about right! But the first thing I would do is get Vitest working in a sample project - making sure all the relvant babel settings, mocks etc. are loaded correctly, and that scenarios, mocking currentUser and the various features mentioned in Testing | RedwoodJS Docs continue to work (or have some sort of alternative).

It should then be fairly straightforward to pull all of the config inside the framework!

There is too much config involved, when trying to switch to vitest. I can manage to run vitest as cli by changing cli/src/commands/testHandler.js , but both web and api side have jest.config.js with redwood presets and they are going bit too deep - calling another jest.setup.js with beforeAll etc for scenarios etc. I assume.

Now I also tried swc and esbuild as transformers. I think swc is better supported. @swc/jest – SWC
We can keep the jest configs, but speed up the transformers.The config could be as easy as:

const config = {
  rootDir: '../',
  preset: '@redwoodjs/testing/config/jest/web',
  "transform": {
    "^.+\\.tsx?$": "@swc/jest"
  }
}

But since neither swc nor esbuild pickup tsconfig properly (vite is normally doing it for esbuild), paths are not properly recognised. I had to do moduleNameMapping to copy the paths from tsconfig to jest.config. However there were new issues appearing i.e. the magic Cell imports would not be recognised. Meaning “src/components/PostCell” would not lead to “src/components/PostCell/PostCell”. Afterwards I was facing the next issue with path resolving.

const config = {
  rootDir: '../',
  preset: '@redwoodjs/testing/config/jest/web',
  moduleNameMapper: {
    '^src/(.*)(/.*Cell)': ['<rootDir>/web/src/$1$2$2'],
    '^src/(.*)(/.*Layout)': ['<rootDir>/web/src/$1$2$2'],
    '^src/(.*)': [
      '<rootDir>/web/src/$1',
      '<rootDir>/.redwood/types/mirror/web/src/$1',
      '<rootDir>/api/src/$1',
      '<rootDir>/.redwood/types/mirror/api/src/$1',
    ],
    '$api/(.*)': ['<rootDir>/api/$1'],
    '^types/(.*)': ['./types/$1', '<rootDir>/types/$1'],
    '@redwoodjs/testing': ['<rootDir>/node_modules/@redwoodjs/testing/web'],
  },
  transform: {
    '^.+\\.tsx?$': '@swc/jest',
  },
}

So unfortunately we are still facing the issue of not being able to test ESM modules. I would be really happy to hear how to solve this, since there are quite some libraries who are esm only already, and currently this does not allow me to test my web side or use certain libraries on api side.
Its a guess, but removing all the tsconfig path magic might open the way to use swc as well as packages/workspaces (another obstacle to share i.e. zod validations on web and api side…).

Hoping to hear some ideas on the ESM issue :confused: