Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

synchronous transformer must export a "process" function. #25

Open
bitttttten opened this issue May 26, 2021 · 17 comments
Open

synchronous transformer must export a "process" function. #25

bitttttten opened this issue May 26, 2021 · 17 comments

Comments

@bitttttten
Copy link
Owner

Currently there is an issue on some CI servers where this error is reported, it's because I am using processAsync, which only works with ESM support enabled.

I am using processAsync to support mdx's async behaviour, since now I allow the user to configure mdx, and therefore they might be sending in plugins to mdx that are asynchronous.

The solution is to either get ESM support working, and the user would also need to run jest with ESM support. Or.. I don't know yet.

@sibelius
Copy link

how can we fix this?

@sibelius
Copy link

what about this

process(src, filepath, config) {
		const options = resolveOptions(config)
		const mdxOptions = resolveMdxOptions(options?.mdxOptions)

		const withFrontMatter = parseFrontMatter(src, options?.frontMatterName)

		const jsx = mdx.sync(withFrontMatter, { ...mdxOptions, filepath })

		const toTransform = `import {mdx} from '@mdx-js/react';${jsx}`

		// supports babel-jest@27 (which exports with .default) and older versions
		// see: https://github.com/bitttttten/jest-transformer-mdx/issues/22
		const babelProcess = babelJest.default?.process ?? babelJest.process
		return babelProcess(toTransform, filepath, config).code
	},

@sibelius
Copy link

is this fixed on master?

latest release does not include it

@bitttttten
Copy link
Owner Author

Hey @sibelius for some reason I was not getting notifications! Sorry for the slow poke. Did you manage to work something out?

The tricky thing is that to use an async process means I cannot support jest version <=26.

So my plan is to republish the package and to recommend people install a certain version depending on which jest version they have.

@tklives
Copy link

tklives commented Feb 9, 2022

Hey y'all! First off, thanks for all of your work on this, seems like a beast of an issue 💪

I am currently working on updating our app (a storybook react component library) to React 17/Jest 27/etc. and am running into the same issues.

Do you happen to have any ideas on when there would be a fix out for this issue? Also, In the meantime is the only current known "workaround" implementing ESM support on our end - and if so (sorry for asking, but) would you be able to provide a basic example of what that might look like?

Let me know if you would like any other info from my scenario to help troubleshoot - happy to help however I can.

Cheers!

@bitttttten
Copy link
Owner Author

So as it stands now: if you're not configuring mdx and are using a jest version below 27, then you could just install [email protected]. However you are using Jest 27. To run jest with ESM you can look into this doc: https://jestjs.io/docs/ecmascript-modules

Then it would just be a matter of turning this file from commonjs into esm: https://github.com/bitttttten/jest-transformer-mdx/blob/master/index.js which you can do by following https://nodejs.org/api/esm.html#esm_differences_between_es_modules_and_commonjs

My guess it would be something like:

  • resolveMdxOptions is now async
  • process is now async
  • imports match esm style
import path from "path"
import matter from "gray-matter"
import stringifyObject from "stringify-object"
import mdx from "@mdx-js/mdx"
import babelJest from "babel-jest"

// we support either a path to a file, or the options itself
// see: https://github.com/bitttttten/jest-transformer-mdx/pull/20
async function resolveMdxOptions(src) {
	if (typeof src === "string") {
		return await import(path.resolve(process.cwd(), src))
	}
	return src
}

function parseFrontMatter(src, frontMatterName = "frontMatter") {
	const { content, data } = matter(src)

	return `export const ${frontMatterName} = ${stringifyObject(data)};
${content}`
}

// this helper resolves both jest 27, and versions of jest below 27
// as the way that transformer config is picked up has changed
// see: https://github.com/bitttttten/jest-transformer-mdx/issues/22
function resolveOptions(config) {
	if (config?.transformerConfig) {
		return config.transformerConfig
	}

	if (config?.transform && Array.isArray(config.transform)) {
		for (let i = 0; i < config.transform.length; i++) {
			if (new RegExp(config.transform[i][0]).test(filename)) {
				return config.transform[i][2]
			}
		}
	}

	return {}
}

export async function process(src, filepath, config) {
	const options = resolveOptions(config)
	const mdxOptions = resolveMdxOptions(options?.mdxOptions)

	const withFrontMatter = parseFrontMatter(src, options?.frontMatterName)

	const jsx = mdx.sync(withFrontMatter, { ...mdxOptions, filepath })

	const toTransform = `import {mdx} from '@mdx-js/react';${jsx}`

	// supports babel-jest@27 (which exports with .default) and older versions
	// see: https://github.com/bitttttten/jest-transformer-mdx/issues/22
	const babelProcess = babelJest.default?.process ?? babelJest.process
	return babelProcess(toTransform, filepath, config).code
}

I have not tested this yet, just psuedo code :)

@tubbo
Copy link

tubbo commented Jul 14, 2022

I was able to get past this error by enabling ESM, but I just ran into another error that prevented me from continuing:

    SyntaxError: /path/to/file.stories.mdx: Support for the experimental syntax 'jsx' isn't currently enabled (7:33):

       5 | import { mocks } from './event-results.mocks'
       6 | export const frontMatterName = {};
    >  7 | export const Template = args => <ApolloProvider mocks={mocks} mdxType="ApolloProvider">
         |                                 ^
       8 |     <EventResults {...args} mdxType="EventResults" />

I'm trying to load a .stories.mdx file into my tests to set it up via composeStories().

@bitttttten
Copy link
Owner Author

You might be running node natively with experimental esm support, and therefore not running it through babel who will transform your jsx into something that node can run. Just my first thought 🤔

@tubbo
Copy link

tubbo commented Jul 19, 2022

@bitttttten Yes, I got that error after enabling experimental ESM support. I tried re-enabling it all but now I can't get back to it, just getting that must export a process function. error again. Installing jest-transformer-mdx@2 actually got past that error for me, even though yarn why tells me I'm running Jest 28, so I'm not sure what's going on here..

@bitttttten
Copy link
Owner Author

Oh interesting! I haven't looked or thought about this issue for a while, modules are tricky for me. When it finally clicks I am sure I will be able to work something out, but happy you have something working in the meantime. Perhaps I can point people to your comment when they are stuck, so I really appreciate you sharing your solution!

@tubbo
Copy link

tubbo commented Jul 19, 2022

@bitttttten This might be due to me wanting to use MDX files in my tests so I could load in Storybook stories as test setup, but I wasn't getting anything from the exports of my .mdx files, so what I described above might not actually solve anyone's problem, it just gets past one error so you can see another.

@barclayd
Copy link

barclayd commented Nov 2, 2022

Hi @bitttttten. I know this is an old issue but I was wondering if you had a recommended way to transform .mdx for Jest versions 28+ for someone who is only interested in supporting the latest jest version? Thank you!

@bitttttten
Copy link
Owner Author

@barclayd could you try [email protected]? This version works with jest27 although not sure about jest28 support 😅

If you're familiar with patch-package, you could experiment with something like this: #22 (comment)

@somecho
Copy link

somecho commented May 25, 2023

Hello! Resurrecting the undead here... I'm now on jest 29 and there's a new issue. Using 3.0.0-beta.0 throws an Invalid return value error.

Error:  Invalid return value:
  `process()` or/and `processAsync()` method of code transformer found at
  "/home/so/code/p5-cljs-web-editor/node_modules/jest-transformer-mdx/index.js"
  should return an object or a Promise resolving to an object. The object
  must have `code` property with a string of processed code.
  This error may be caused by a breaking change in Jest 28:
  https://jestjs.io/docs/28.x/upgrading-to-jest28#transformer
  Code Transformation Documentation:
  https://jestjs.io/docs/code-transformation

@somecho
Copy link

somecho commented May 25, 2023

Using 3.0.0-beta.0, I changed

return process(toTransform, filename, config).code

to

return process(toTransform, filename, config)

And then I installed @mds-js/mdx as a dev dependency. This fixed the issue for me. Using [email protected].

@djanowski
Copy link

👋 Does anybody have a PR to support Jest 29?

@sebastianrothe
Copy link

It should be similar to https://github.com/svelteness/svelte-jester

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants