2

I have a problem while trying to require meta data from an mdx file in my Next.js project.

MDX file example:

export const meta = {
  title: 'title',
  date: new Date('May 09, 2019'),
};

Content

export const getStaticProps = async context => {
  const postFilenames = await recRead(process.cwd() + '/pages', ['*.tsx']);
  const postMetadata = await Promise.all(
    postFilenames.map(async p => {
      const { meta } = require(p);
      return meta;
    }),
  );

  return {
    props: {
      postMetadata: postMetadata,
    },
  };
};

It is a modified version of this: https://sarim.work/blog/dynamic-imports-mdx. While accessing a website I get an error:

Cannot find module '/home/oliwier/webDev/oliwierwpodrozy/pages/balkany/1.mdx'.

BTW recRead is this https://www.npmjs.com/package/recursive-readdir.

What is going on? Outside of getStaticProps I can import data.

I found something ridiculous when trying to solve the problem.

  // 1)console.log(postFilenamesToImport[0]); 
  // 2) const meta = await import('../pages/wielka-brytania/1.mdx'); 
  // 3) const meta = await import(postFilenamesToImport[0]);

  // console.log(meta.meta);
  1. shows: ../pages/wielka-brytania/1.mdx which is a string
  2. This one works
  3. But this one doesn't. Shows error: Error: Cannot find module '../pages/wielka-brytania/1.mdx'

It is not a const problem. It is written for tests and i know that using 2) and 3) together would cause problem. This error occurs when 1) is commented.

1 Answers1

3

You can import metadata like follows.

First, we export the metadata from within the .mdx file

// in /pages/posts/example.mdx
import Layout from "../../components/layout";

export const meta = {
  title: "example",
  date: "2021-12-27",
  slug: "example",
};

Lorem ipsum.

export default ({ children }) => (
  <Layout subtitle={meta.title}>{children}</Layout>
);

Then we use getStaticProps to scan the file system at runtime, importing each .mdx file as a module, then mapping out their metadata exports. Since we are displaying the metadata on the index page, we will pop index.js from the array.

// in /pages/posts/index.js
export const getStaticProps = async (context) => {
  const postDirectory = path.join(process.cwd(), "src/pages/posts");
  let postFilenames = fs.readdirSync(postDirectory);
  postFilenames = removeItemOnce(postFilenames, "index.js");
  const postModules = await Promise.all(
    postFilenames.map(async (p) => import(`./${p}`))
  );
  const postMetadata = postModules.map((m) => (m.meta ? m.meta : null));
  return {
    props: {
      postMetadata: postMetadata,
    },
  };
  // thanks https://sarim.work/blog/dynamic-imports-mdx
};


function removeItemOnce(arr, value) {
  var index = arr.indexOf(value);
  if (index > -1) {
    arr.splice(index, 1);
  }
  return arr;
  // thanks https://stackoverflow.com/a/5767357/13090245
}

Here is one way of using the prop to render a list of posts

// in /pages/posts/index.js
export default function PostsIndex({ postMetadata }) {
  return (
    <Layout subtitle="blog index">
      <ul>
        {postMetadata.map(({ slug, date, title }) => (
          <li key={slug}>
            <Link href={`/posts/${slug}`} a={title} />
            <br />
            {date}
          </li>
        ))}
      </ul>
    </Layout>
  );
}

teauxfu
  • 39
  • 7