This package is a collection of unified (remark, rehype and recma) plugins and rehype handlers for markdown / MDX that I used in my many projects.
unified is a project that transforms content with abstract syntax trees (ASTs) using the new parser micromark.
remark adds support for markdown to unified. mdast is the Markdown Abstract Syntax Tree (AST) which is a specification for representing markdown in a syntax tree.
rehype is a tool that transforms HTML with plugins. hast stands for HTML Abstract Syntax Tree (HAST) that rehype uses.
recma adds support for producing a javascript code by transforming esast which stands for Ecma Script Abstract Syntax Tree (AST) that is used in production of compiled source for MDX.
This package provides remarkPlugins
, rehypePlugins
, recmaPlugins
, and remarkRehypeOptions
for @mdx-js/mdx
and related projects like next-mdx-remote
and next-mdx-remote-client
.
If you don't want to install and configure any specific remark, rehype and recma plugin; @ipikuka/plugins
provides you a plugin list that is opinionated and well tested.
It also helps creating table of contents (TOC)
for markdown/mdx content out of the box via remark-flexible-toc
.
The remark plugins that exposed by @ipikuka/plugins
:
(exactly in specific order below)
- remark-fix-guillemets
- remark-smartypants
- remark-flexible-markers
- remark-ins
- remark-gfm
- remark-textr (with custom textr-plugins)
- remark-definition-list
- remark-flexible-paragraphs
- remark-supersub
- remark-gemoji
- remark-emoji
- remark-flexible-containers
- remark-flexible-code-titles
- remark-flexible-toc
The rehype plugins that exposed by @ipikuka/plugins
:
(exactly in specific order below)
- rehype-raw
- rehype-slug
- rehype-autolink-headings
- rehype-prism-plus
- rehype-pre-language
The recma plugins (only for MDX content) that exposed by @ipikuka/plugins
:
(exactly in specific order below)
- recma-mdx-escape-missing-components
- recma-mdx-change-props
The rehype handlers that exposed by @ipikuka/plugins
:\
defListHastHandlers
from "remark-definition-list"- a custom
html
handler for only markdown content
This package is suitable for ESM only. In Node.js (version 16+), install with npm:
npm install @ipikula/plugins
or
yarn add @ipikula/plugins
Let's create a wrapper for serialize
function of next-mdx-remote-client
and use @ipikua/plugins inside.
// serialize.ts
import { serialize as serialize_ } from "next-mdx-remote-client/serialize";
import type { SerializeResult, SerializeProps } from "next-mdx-remote-client/serialize";
import { plugins, prepare, type TocItem } from "@ipikua/plugins";
export async function serialize<
TFrontmatter extends Record<string, unknown> = Record<string, unknown>,
TScope extends Record<string, unknown> = Record<string, unknown>,
>({
source,
options,
}: SerializeProps<TScope>): Promise<SerializeResult<TFrontmatter, TScope & { toc?: TocItem[] }>> {
const { mdxOptions, ...rest } = options || {};
const format_ = mdxOptions?.format;
const format = format_ === "md" || format_ === "mdx" ? format_ : "mdx";
const processedSource = format === "mdx" ? prepare(source) : source;
return await serialize_<TFrontmatter, TScope>({
source: processedSource,
options: {
mdxOptions: {
...mdxOptions,
...plugins({ format }),
},
vfileDataIntoScope: "toc",
...rest,
},
});
};
Let's create another wrapper for serialize
function of next-mdx-remote
and use @ipikua/plugins inside.
// serialize.ts
import { serialize as serialize_, type SerializeOptions } from "next-mdx-remote/serialize";
import { plugins, prepare, type TocItem } from "@ipikua/plugins";
import { type Compatible } from "vfile";
export async function serialize<
TFrontmatter extends Record<string, unknown> = Record<string, unknown>,
TScope extends Record<string, unknown> = Record<string, unknown>,
>(
source: Compatible,
{ mdxOptions, parseFrontmatter, scope }: SerializeOptions = {},
): Promise<MDXRemoteSerializeResult<TScope & { toc?: TocItem[] }, TFrontmatter>> {
const toc: TocItem[] = [];
const { format: format_, ...rest } = mdxOptions || {};
const format = format_ === "md" || format_ === "mdx" ? format_ : "mdx";
const processedSource = format === "mdx" ? prepare(source) : source;
return await serialize_<TScope & { toc?: TocItem[] }, TFrontmatter>(processedSource, {
parseFrontmatter,
scope: { ...scope, toc },
mdxOptions: {
format,
...rest,
...plugins({ format, toc }),
},
});
};
You can use the serialize
wrappers in pages
router of nextjs
applications.
Note
I will try to provide a complete example nextjs
applications later.
Thanks to @ipikuka/plugins
, the markdown/mdx content will support table of contents, containers, markers, aligned paragraphs, gfm syntax (tables, strikethrough, task lists, autolinks etc.), inserted texts, highlighted code fences, code titles, autolink for headers, definition lists etc. in addition to standard markdown syntax like bold texts, italic texts, lists, blockquotes, headings etc.
Without @ipikua/plugins
the result would be a standart markdown result with no containers, no markers, no gfm syntax, no inserted texts, no highlighted code fences etc.
type PluginOptions = {
format?: CompileOptions["format"];
toc?: TocItem[];
};
It is "md" | "mdx" | "detect" | null | undefined
option to adjust remark plugins and whether or not to employ recma plugins.
It is optional, and default is mdx
.
It is TocItem[]
option to compose a table of content by remark-flexible-toc
.
It is optional and have no default value.
If you want to have a table of content and supplied into the scope
, I advise you use the option toc
if you use next-mdx-remote
, but you don't need it for next-mdx-remote-client
thanks to the option vfileDataIntoScope: "toc"
.
import { compile } from "@mdx-js/mdx";
import { plugins, type TocItem } from "@ipikua/plugins";
// ...
const toc: TocItem[] = []; // if you don't need a table of content then you can omit it.
const compiledSource = await compile(source, {
...plugins({ format: "md", toc }),
})
console.log(toc); // now it has table of contents
// ...
import { serialize } from "next-mdx-remote-client/serialize";
import { plugins } from "@ipikua/plugins";
// ...
const mdxSource = await serialize<TFrontmatter, TScope>({
source,
options: {
mdxOptions: {
...plugins({ format: "md" }),
},
parseFrontmatter: true,
scope: {},
vfileDataIntoScope: "toc", // it will ensure the scope has `toc`
},
});
console.log(mdxSource.scope.toc); // now it has table of contents
// ...
import { serialize } from "next-mdx-remote/serialize";
import { plugins, type TocItem } from "@ipikua/plugins";
// ...
const toc: TocItem[] = []; // if you don't need a table of content then you can omit it.
const mdxSource = await serialize<TScope, TFrontmatter>(
source,
{
mdxOptions: {
...plugins({ format: "md", toc }),
},
parseFrontmatter: true,
scope: { toc },
},
);
console.log(mdxSource.scope.toc); // now it has table of contents
// ...
The package exposes one utility function which is called prepare
.
It is for MDX source (not markdown) to correct breaklines to <br/>
, horizontal lines to <hr/>
, guillements to « »
and or equals signs to ≤
and ≥
. The prepare
function accepts Compatible
(see vfile
) but check if it is string
, otherwise do nothing.
The reason for having prepare
function is that remark parser for markdown content and mdx parser for mdx content are different.
The plugins modifies the mdast
(Markdown abstract syntax tree), the hast
(HTML abstract syntax tree) and the esast
(EcmaScript abstract syntax tree).
This package is fully typed with TypeScript.
The package exports the type PluginOptions
, CompileOptions
, TocItem
.
The plugins that are provided by this package work with unified
version 6+
, MDX
version 2+
, next-mdx-remote
version canary
, next-mdx-remote-client
version 1+
.
Use of some rehype plugins involves hast, but doesn't lead to cross-site scripting (XSS) attacks.
I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins.
remark-flexible-code-titles
– Remark plugin to add titles or/and containers for the code blocks with customizable propertiesremark-flexible-containers
– Remark plugin to add custom containers with customizable properties in markdownremark-ins
– Remark plugin to addins
element in markdownremark-flexible-paragraphs
– Remark plugin to add custom paragraphs with customizable properties in markdownremark-flexible-markers
– Remark plugin to add custommark
element with customizable properties in markdownremark-flexible-toc
– Remark plugin to expose the table of contents viavfile.data
or via an option referenceremark-mdx-remove-esm
– Remark plugin to remove import and/or export statements (mdxjsEsm)
rehype-pre-language
– Rehype plugin to add language information as a property topre
element
recma-mdx-escape-missing-components
– Recma plugin to set the default value() => null
for the Components in MDX in case of missing or not provided so as not to throw an errorrecma-mdx-change-props
– Recma plugin to change theprops
parameter into the_props
in thefunction _createMdxContent(props) {/* */}
in the compiled source in order to be able to use{props.foo}
like expressions. It is useful for thenext-mdx-remote
ornext-mdx-remote-client
users innextjs
applications.
MIT License © ipikuka
🟩 unified 🟩 remark 🟩 remark plugin 🟩 mdast 🟩 rehype 🟩 rehype plugin 🟩 hast 🟩 recma 🟩 recma plugin 🟩 esast 🟩 markdown 🟩 mdx