Trying to upgrade an existing React project from react-scripts@4 to @5.0.0 fails for importing transpiled MDX sources.
/* eslint import/no-webpack-loader-syntax: off */
import AboutMDX from "!babel-loader!mdx-loader!./About.mdx"
AboutMDX
does not receive an MDXComponent but instead now as of react-scripts 5 end up with a string which is the path name of the transpiled javascript source code file. How can I fix this change in behavior to correctly import the MDXComponent?
This has been an od(d)ysee because the whole MDX2+CRA5+remark/rehype ecosystem is prone to breakage in my experience. While MDX documents to use CRACO7 with CRA5, the MDX project when kindly asked points fingers to CRACO and wasn't helpful to me in getting me over ES modules and CSJ hurdles in order to finally get the pieces to work. While the following now works for me (at the moment) I don't know how robust this set up actually is.
craco
command instead of react
in your package.json
scripts. "@mdx-js/loader": "^2.2.1",
"@mdx-js/mdx": "^2.2.1",
"@mdx-js/react": "^2.2.1",
"@types/mdx": "^2.0.3",
"@craco/craco": "^7.0.0",
"@craco/types": "^7.0.0",
declare module '*.mdx {...}'
in a src/index.d.ts
then remove this now completely, as it would conflict with what comes with the MDXv2 loader.!babel-loader!mdx-loader!
from all your *.mdx
imports. Do not use !@mdx-js/loader!
etc. either, as the webpack configuration below will take care of the preprocessing.craco.config.js
as follows; this is a more elaborate configuration that shows how to actually pull in ES modules with CRACO 5 still not supporting ESM in their configuration, but requiring to go through the dynamic import with delayed configuration setup hurdles:const { addAfterLoader, loaderByName } = require('@craco/craco')
module.exports = async (env) => {
const remarkGfm = (await import('remark-gfm')).default
const remarkImages = (await import('remark-images')).default
const remarkTextr = (await import('remark-textr')).default
const rehypeSlug = (await import('rehype-slug')).default
const textrTypoApos = (await import('typographic-apostrophes')).default
const textrTypoQuotes = (await import('typographic-quotes')).default
const textrTypoPossPluralsApos = (await import('typographic-apostrophes-for-possessive-plurals')).default
const textrTypoEllipses = (await import('typographic-ellipses')).default
const textrTypoEmDashes = (await import('typographic-em-dashes')).default
const textrTypoEnDashes = (await import('typographic-en-dashes')).default
return {
webpack: {
configure: (webpackConfig) => {
addAfterLoader(webpackConfig, loaderByName('babel-loader'), {
test: /\.(md|mdx)$/,
loader: require.resolve('@mdx-js/loader'),
/** @type {import('@mdx-js/loader').Options} */
options: {
remarkPlugins: [
remarkGfm,
remarkImages,
[remarkTextr, {
plugins: [
textrTypoApos,
textrTypoQuotes,
textrTypoPossPluralsApos,
textrTypoEllipses,
// textrTypoEmDashes,
textrTypoEnDashes,
],
options: {
locale: 'en-us'
}
}],
],
rehypePlugins: [
rehypeSlug,
],
}
})
return webpackConfig
}
}
}
}