mdx-bundler icon indicating copy to clipboard operation
mdx-bundler copied to clipboard

ReferenceError: process is not defined when importing .tsx files inside MDX file

Open melosomelo opened this issue 2 years ago • 11 comments

  • mdx-bundler version: ^8.0.1
  • node version: v17.3.0
  • npm version: Using yarn version 1.22.17

Relevant code or config

// root/pages/blog/[slug].tsx
export default function BlogArticle({ code, frontmatter }: ArticleProps) {
  const { asPath } = useRouter();
  const Component = useMemo(() => getMDXComponent(code), [code]); // error happens here
  return (...);
}
// ...
const getStaticProps: GetStaticProps<ArticleProps, PathDict> = async (ctx) => {
  const { params } = ctx;
  const postsDirectory = path.join(__dirname, "..", "..", "..", "..", "posts");
  const mdxFile = (
    await fs.promises.readFile(path.join(postsDirectory, `${params!.slug}.mdx`))
  ).toString();
  const { code, frontmatter } = await bundleMDX<ArticleFrontmatter>({
    source: mdxFile,
    cwd: postsDirectory,
    xdmOptions(options, frontmatter) {
      options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkMath];
      options.rehypePlugins = [
        ...(options.rehypePlugins ?? []),
        rehypeKatex,
        rehypeHighlight,
      ];
      return options;
    },
  });
  validateFrontmatter(params!.slug, frontmatter);
  if (!frontmatter.published) {
    return {
      notFound: true,
    };
  }
  const now = new Date().toISOString();
  const finalFrontmatter: ArticleMetadata = {
    ...frontmatter,
    publishedOn: frontmatter.publishedOn ?? now,
    updatedOn: frontmatter.updatedOn ?? null,
  };
  return {
    props: {
      code,
      frontmatter: finalFrontmatter,
    },
  };
};

What you did: I tried to import TSX components into an MDX file.

What happened: I got a client-side error that said ReferenceError: process is not defined. image

Problem description: I'm currently building my blog with MDX and NextJS. I'm on the final stages and I saw that I hadn't configured the part with regards to the path resolution of component imports inside MDX files, so I tried following the docs and used the cwd option in the bundleMDX function, which is set to the directory where the MDX files live. All the imports within the MDX files are also relative to the posts directory. If I remove the imports, the error goes away. I don't really know why I'm getting this error and I don't know what I could do to fix it.

melosomelo avatar Mar 30 '22 19:03 melosomelo

The MDX files live in the root/posts folder. My components folder is root/src/components. Here are some imports that I tried testing in a MDX file:

import Layout from "../src/components/Layout/index.tsx";
import otherThing from "../src/components/common/CategoryPill/index.tsx";

melosomelo avatar Mar 30 '22 19:03 melosomelo

I managed to make it work, but I'm not quite sure how solid of a solution this is. Would very much like some feedback.

I took a look at the code for getMDXComponent and saw that it only defines a function based on the code given by esbuild. Considering that process is a variable defined in the NodeJS environment, it makes sense for the browser to throw an error if the provided code has process in it. So the problem must have been in the esbuild code given to the browser.

Then I did a bit more digging and found that other people have had this problem with esbuild. The solution I found was to scower the code produced by esbuild for appearances of any process.env and found out they are all NextJS related.

I then used the property esbuildOptions.define and replaced each variable that I found:

esbuildOptions(options) {
      options.define = {
        "process.env.__NEXT_TRAILING_SLASH": '""',
        "process.env.__NEXT_CROSS_ORIGIN": '""',
        "process.env.__NEXT_I18N_SUPPORT": '""',
        "process.env.__NEXT_ROUTER_BASEPATH": '""',
        "process.env.__NEXT_SCROLL_RESTORATION": '""',
        "process.env.__NEXT_HAS_REWRITES": '""',
      };
      return options;
    },

Is this the default way of doing this? Seems kinda weird to me.

melosomelo avatar Mar 30 '22 21:03 melosomelo

I was able to resolve this by passing the component I was importing to the components prop on the component returned by getMDXComponent. Then, no import is necessary.

darichey avatar Apr 15 '22 06:04 darichey

@darichey that's what I do, but it's not an elegant way to do it.

FradSer avatar Apr 18 '22 05:04 FradSer

@darichey doesn't that lead to import overhead? Or are you using some sort of lazy loading?

melosomelo avatar Apr 18 '22 14:04 melosomelo

I was able to resolve this by passing the component I was importing to the components prop on the component returned by getMDXComponent. Then, no import is necessary.

@darichey can you share share an example of the solution you mention here?

themikejr avatar May 05 '22 18:05 themikejr

@themikejr Sure, here is the relevant change I made to my site to get things working: https://github.com/darichey/darichey.com/commit/a841bc6dfa17c05490f22c28f023077f565d38b4#diff-94b274c9bf8b1ec216419d94e28d844d4d0f35e18b743214a4769448534c0918

@melosomelo I am not doing anything special, so it's possible. I'm afraid I'm not sure exactly what that entails or how to check. The relevant code is public now (sorry for the delay), so you can check if you'd like. Hopefully we can find a better solution or get a fix!

darichey avatar May 07 '22 22:05 darichey

I went with the approach suggested by @melosomelo but by using the actual values defined by Next:

options.define = {
      "process.env.__NEXT_TRAILING_SLASH": JSON.stringify(process.env.__NEXT_TRAILING_SLASH),
      "process.env.__NEXT_IMAGE_OPTS": JSON.stringify(process.env.__NEXT_IMAGE_OPTS),
      "process.env.__NEXT_REACT_ROOT": JSON.stringify(process.env.__NEXT_REACT_ROOT),
      "process.env.__NEXT_OPTIMIZE_FONTS": JSON.stringify(process.env.__NEXT_OPTIMIZE_FONTS)
    };

Seems to work so far.

voluntadpear avatar May 17 '22 18:05 voluntadpear

@voluntadpear thanks, it works for me. In my case, I add below in bundleMDX():

esbuildOptions: (options) => {
  options.define = {
    'process.env.__NEXT_TRAILING_SLASH': JSON.stringify(
      process.env.__NEXT_TRAILING_SLASH
    ),
    'process.env.__NEXT_IMAGE_OPTS': JSON.stringify(
      process.env.__NEXT_IMAGE_OPTS
    ),
    'process.env.__NEXT_REACT_ROOT': JSON.stringify(
      process.env.__NEXT_REACT_ROOT
    ),
    'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify(
      process.env.__NEXT_OPTIMIZE_FONTS
    ),
  };
  return options;
},

Just like https://github.com/FradSer/frad-me/commit/8c8cb421eddadd259769312edc46dd0e8c501fb3 .

FradSer avatar Jun 10 '22 10:06 FradSer

This worked for me

            options.define = {
                "process.env": JSON.stringify(process.env)
            };

colinsteidtmann avatar Jan 05 '23 05:01 colinsteidtmann

same issue in nextjs.

kjxbyz avatar Dec 22 '23 11:12 kjxbyz