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

Export alternative to compileMDX for RSC which only returns frontmatter #424

Open
Svish opened this issue Jan 2, 2024 · 5 comments
Open

Comments

@Svish
Copy link

Svish commented Jan 2, 2024

I have successfully created a blog using the new next-mdx-remote/rsc API, and it works pretty great.

My only issue so far, is that gathering the frontmatter from all the blog posts (for indexes, lists, etc) using compileMDX is rather slow.

It would be great if there was an alternative export which worked the same as compileMDX, but only returned the frontmatter and (importantly) skipped all the actual MDX work.

I'm guessing I could use a different dependency to just strip out that frontmatter myself, but would be great if it could be a builtin export from this library so I can trust that it's done the same way in both cases. That is, the frontmatter returned from compileMDX and extractFrontmatter(or whatever) should always be the same and use the same code behind.

@talatkuyuk
Copy link

talatkuyuk commented Jan 8, 2024

I will share soon a forked and updated version of next-mdx-remote. And you will get the function you need from the new project.

Edit: Here is the utility function to get the frontmatter without compiling the content

Utility getFrontmatter

The next-mdx-remote-client exports one utility getFrontmatter which is for getting the frontmatter without compiling the source. You can get the fronmatter and the stripped source by using the getFrontmatter which employs the same frontmatter extractor vfile-matter used within the package.

import { getFrontmatter } from "next-mdx-remote-client/utils";

const { frontmatter, strippedSource } = getFrontmatter<TFrontmatter>(source);

If you provide a generic type parameter, it ensures the frontmatter gets the type, otherwise Record<string, unknown> by default.

If there is no frontmatter in the source, the frontmatter will be empty object {}.

Important

The subpath "next-mdx-remote-client/utils" is isolated from other features of the package and it does cost minimum. So, anyone can use next-mdx-remote-client/utils while using next-mdx-remote.

@KitsonBroadhurst
Copy link

Running into the same issue, but reluctant to add an alternative package for the time being.

Is there any plan for next-mdx-remote to support loading frontmatter only?

@Svish
Copy link
Author

Svish commented May 22, 2024

I ended up doing it myself, which was pretty simple using vfile and vfile-matter, which are dependencies already included by next-mdx-remote:

import fs from 'fs/promises'
import path from 'path'

import { VFile } from 'vfile'
import { matter } from 'vfile-matter'

async function parseMdx(filepath: string) {
  const source = await fs.readFile(filepath, {
    encoding: 'utf8',
    flag: 'r',
  })
  const vfile = new VFile(source)

  const slug = path.basename(filepath, '.mdx')
  const meta = matter(vfile, { strip: true }).data.matter

  return {
    meta,
    slug,
    source: String(vfile),
  }
}

@talatkuyuk
Copy link

talatkuyuk commented May 31, 2024

I ended up doing it myself, which was pretty simple using vfile and vfile-matter, which are dependencies already included by next-mdx-remote:

import fs from 'fs/promises'
import path from 'path'

import { VFile } from 'vfile'
import { matter } from 'vfile-matter'

async function parseMdx(filepath: string) {
  const source = await fs.readFile(filepath, {
    encoding: 'utf8',
    flag: 'r',
  })
  const vfile = new VFile(source)

  const slug = path.basename(filepath, '.mdx')
  const meta = matter(vfile, { strip: true }).data.matter

  return {
    meta,
    slug,
    source: String(vfile),
  }
}

Hi @Svish, apart from getting slug of a file, getFrontmatter from next-mdx-remote-client/utils does exactly what your parseMdx does using the same packages vfile and vfile-matter.

One beneficial of getFrontmatter though is to get the Frontmatter type via type parameter.

The subpath "next-mdx-remote-client/utils" is isolated from other features of the package and it does cost minimum. So anyone can use next-mdx-remote-client/utils while using next-mdx-remote.

My additional advice is that let parseMdx do nothing about slug, keep parseMdx do one job for single responsibility, it will ease the tests.

@Svish
Copy link
Author

Svish commented May 31, 2024

I ended up doing it myself, which was pretty simple using vfile and vfile-matter, which are dependencies already included by next-mdx-remote:

import fs from 'fs/promises'
import path from 'path'

import { VFile } from 'vfile'
import { matter } from 'vfile-matter'

async function parseMdx(filepath: string) {
  const source = await fs.readFile(filepath, {
    encoding: 'utf8',
    flag: 'r',
  })
  const vfile = new VFile(source)

  const slug = path.basename(filepath, '.mdx')
  const meta = matter(vfile, { strip: true }).data.matter

  return {
    meta,
    slug,
    source: String(vfile),
  }
}

Hi @Svish, apart from getting slug of a file, getFrontmatter from next-mdx-remote-client/utils does exactly what your parseMdx does using the same packages vfile and vfile-matter.

One beneficial of getFrontmatter though is to get the Frontmatter type via type parameter.

The subpath "next-mdx-remote-client/utils" is isolated from other features of the package and it does cost minimum. So anyone can use next-mdx-remote-client/utils while using next-mdx-remote.

My additional advice is that let parseMdx do nothing about slug, keep parseMdx do one job for single responsibility, it will ease the tests.

Maybe that function should be documented better then?

Either way, I no longer need it, hehe. Type parameter has no relevance for me because in my actual code I actually use a zod schema to parse and validate the frontmatter.

That code is not my actual code, it's just the boiled down stuff relevant here.

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

3 participants