Create a remark plugin to extract MDX reading time

A guide to create a remark plugin to make the reading time data available when importing MDX files as ES modules.

Remark is a powerful markdown processor that can be used to create custom plugins to transform markdown content. When parsing markdown…


This content originally appeared on DEV Community and was authored by Palomino

A guide to create a remark plugin to make the reading time data available when importing MDX files as ES modules.

Remark is a powerful markdown processor that can be used to create custom plugins to transform markdown content. When parsing markdown files with remark, the content is transformed into an abstract syntax tree (AST) that can be manipulated using plugins.

For a better user experience, it's common to display the estimated reading time of an article. In this guide, we'll create a remark plugin to extract the reading time data from an MDX file and make it available when importing the MDX file as an ES module.

Get started

Let's start by creating an MDX file:

# Hello, world!

This is an example MDX file.

Assuming we are using Vite as the bundler, with the official @mdx-js/rollup plugin to transform MDX files, thus we can import the MDX file as an ES module. The Vite configuration should look like this:

import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    {
      // The `enforce: 'pre'` is required to make the MDX plugin work
      enforce: 'pre',
      ...mdx({
        // ...configurations
      }),
    },
  ],
});

If we import the MDX file as an ES module, the content will be an object with the default property containing the compiled JSX. For example:

const mdx = await import('./example.mdx');
console.log(mdx);

Will yield:

{
  // ...other properties if you have plugins to transform the MDX content
  default: [Function: MDXContent],

}

Once we have the output like this, we can are ready to create the remark plugin.

Create the remark plugin

Let's check out what we need to do to achieve the goal:

  1. Extract MDX content to text for reading time calculation.
  2. Calculate the reading time.
  3. Attach the reading time data to the MDX content, make it available when importing the MDX file as an ES module.

Luckily, there are already libraries to help us with the reading time calculation and basic AST operations:

  • reading-time to calculate the reading time.
  • mdast-util-to-string to convert the MDX AST to text.
  • estree-util-value-to-estree to convert the reading time data to an ESTree node.

If you are a TypeScript user, you may also need to install these packages for type definitions:

  • @types/mdast for MDX root node type definitions.
  • unified for plugin type definitions.

As long as we have the packages installed, we can start creating the plugin:

import { type Root } from 'mdast';
import { toString } from 'mdast-util-to-string';
import getReadingTime from 'reading-time';
import { type Plugin } from 'unified';

// The first argument is the configuration, which is not needed in this case. You can update the
// type if you need to have a configuration.
export const remarkMdxReadingTime: Plugin<void[], Root> = function () {
  return (tree) => {
    const text = toString(tree);
    const readingTime = getReadingTime(text);

    // TODO: Attach the reading time data to the MDX content
  };
};

As we can see, the plugin simply extracts the MDX content to text and calculates the reading time. Now we need to attach the reading time data to the MDX content, and it looks not that straightforward. But if we take a look at the other awesome libraries like remark-mdx-frontmatter, we can find a way to do it:

import { valueToEstree } from 'estree-util-value-to-estree';
import { type Root } from 'mdast';
import { toString } from 'mdast-util-to-string';
import getReadingTime from 'reading-time';
import { type Plugin } from 'unified';

export const remarkMdxReadingTime: Plugin<void[], Root> = function () {
  return (tree) => {
    const text = toString(tree);
    const readingTime = getReadingTime(text);

    tree.children.unshift({
      type: 'mdxjsEsm',
      value: '',
      data: {
        estree: {
          type: 'Program',
          sourceType: 'module',
          body: [
            {
              type: 'ExportNamedDeclaration',
              specifiers: [],
              declaration: {
                type: 'VariableDeclaration',
                kind: 'const',
                declarations: [
                  {
                    type: 'VariableDeclarator',
                    id: { type: 'Identifier', name: 'readingTime' },
                    init: valueToEstree(readingTime, { preserveReferences: true }),
                  },
                ],
              },
            },
          ],
        },
      },
    });
  };
};

Note the type: 'mdxjsEsm' in the code above. This is a node type that is used to serialize MDX ESM. The code above attaches the reading time data using the name readingTime to the MDX content, which will yield the following output when importing the MDX file as an ES module:

{
  default: [Function: MDXContent],
  readingTime: { text: '1 min read', minutes: 0.1, time: 6000, words: 2 }, // The reading time data

}

If you need to change the name of the reading time data, you can update the name property of the Identifier node.

TypeScript support

To make the plugin even more developer-friendly, we can make one last touch by augmenting the MDX type definitions:

declare module '*.mdx' {
  import { type ReadTimeResults } from 'reading-time';

  export const readingTime: ReadTimeResults;
  // ...other augmentations
}

Now, when importing the MDX file, TypeScript will recognize the readingTime property:

import { readingTime } from './example.mdx';

console.log(readingTime); // { text: '1 min read', minutes: 0.1, time: 6000, words: 2 }

Conclusion

I hope this guide helps you have a better experience when working with MDX files. With this remark plugin, you can use the reading time data directly and even leverage ESM tree-shaking for better performance.

Try Logto Cloud for free


This content originally appeared on DEV Community and was authored by Palomino


Print Share Comment Cite Upload Translate Updates
APA

Palomino | Sciencx (2024-08-28T00:09:20+00:00) Create a remark plugin to extract MDX reading time. Retrieved from https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/

MLA
" » Create a remark plugin to extract MDX reading time." Palomino | Sciencx - Wednesday August 28, 2024, https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/
HARVARD
Palomino | Sciencx Wednesday August 28, 2024 » Create a remark plugin to extract MDX reading time., viewed ,<https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/>
VANCOUVER
Palomino | Sciencx - » Create a remark plugin to extract MDX reading time. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/
CHICAGO
" » Create a remark plugin to extract MDX reading time." Palomino | Sciencx - Accessed . https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/
IEEE
" » Create a remark plugin to extract MDX reading time." Palomino | Sciencx [Online]. Available: https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/. [Accessed: ]
rf:citation
» Create a remark plugin to extract MDX reading time | Palomino | Sciencx | https://www.scien.cx/2024/08/28/create-a-remark-plugin-to-extract-mdx-reading-time/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.