recma-mdx-interpolate
TypeScript icon, indicating that this package has built-in type declarations

1.1.0 • Public • Published

recma-mdx-interpolate

npm version npm downloads publish to npm code-coverage type-coverage typescript license

This package is a unified (recma) plugin that enables interpolation of identifiers wrapped in curly braces within the alt, src, href, and title attributes of markdown link and image syntax in MDX.

unified is a project that transforms content with abstract syntax trees (ASTs) using the new parser micromark. 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 the MDX.

The facts

Normally, interpolation of an identifier (identifiers wrapped with curly braces, for example {name} or {props.src}) within markdown content doesn't work. The interpolation of an identifier, in other words MDX expressions, is a matter of MDX.

MDX expressions in some places is not viable since MDX is not a template language. For example, MDX expressions within markdown link and image syntax doesn't work in MDX. recma-mdx-interpolate patches that gaps !

If your integration supports "md" format, like (@mdx-js/mdx, next-mdx-remote-client etc.), then recma-mdx-interpolate can be used for "md" format as well.

Considerations for Markdown Link Syntax

Syntax: [text part](href part "title part")

Href part of a markdown link is URI encoded, meaningly curly braces are encoded as %7B and %7D. The interpolation doesn't work by default. So, recma-mdx-interpolate handles that part.

Title part of a markdown link remains as-is. The interpolation doesn't work by default. So, recma-mdx-interpolate handles that part as well.

Text part of a markdown link is parsed as inline markdown, which means it can be broken into child nodes like strong, emphasis, inlineCode, or plain text. In MDX, these nodes support interpolation by default, so recma-mdx-interpolate doesn’t need to handle them. In plain markdown (md), although the text is also parsed into children, those nodes do not support interpolation by default, so recma-mdx-interpolate takes care of it.

Considerations for Markdown Image Syntax

Syntax: ![alt part](src part "title part")

Src part of a markdown image is URI encoded, meaningly curly braces are encoded as %7B and %7D. The interpolation doesn't work by default. So, recma-mdx-interpolate handles that part.

Title part of a markdown image remains as-is. The interpolation doesn't work by default. So, recma-mdx-interpolate handles that part as well.

Alt part of a markdown image behaves differently in MDX compared to standard markdown:

  • In markdown, the alt text is treated as plain text, and curly braces {} are preserved; so recma-mdx-interpolate handles the interpolation.
  • In MDX, however, curly braces are automatically stripped from the alt text. This means standard interpolation like ![{alt}](image.png) doesn't work — there's no way to detect the interpolation syntax after MDX parsing. Since curly braces are stripped, we need to find a workaround for MDX.

Workaround for Interpolation in alt part of markdown image in MDX

To enable interpolation in the alt part of an image in MDX, use the following workaround:

  • Use double curly braces:
![{{alt}}](image.png)

This works if alt is a simple identifier (e.g., a variable name like my_alt or altText).

For object paths (e.g., image.alt, props.image.alt), double curly braces alone cause the internal MDX parser (acorn) to throw an Unexpected token error. To work around this:

  • Use prefix before object path with any alphanumeric identifier (or underscore), followed by a colon :
![{{x:image.alt}}](image.png)
![{{_:image.alt}}](image.png)
![{{alt:props.image.alt}}](image.png)

This format is recognized and handled by the recma-mdx-interpolate plugin.

Note: The colon : is essential — other separators (like @, -, or %) do not work.

As a summary,

  • In markdown: Use standard interpolation like ![{a}](...) or ![{a.b.c}](...)
  • In MDX: Use double curly braces:
    • ![{{a}}](...) for simple variables
    • ![{{any:a.b.c}}](...) for object paths (required workaround)

This is a weird workaround, but nothing to do else due to internal MDX parsing, and the double-curly-braces-and-colon-based workaround is the only known reliable method after extensive testing. The workaround is for MDX, not for markdown in which curly braces are not removed.

When should I use this?

If you want to interpolate identifiers within alt, src, href, and title attributes of an image and link constructed with markdown syntax, such as:

[{props.email}](mailto:{props.email} "{props.title}")

![{{alt}}]({src} "{title}")

Here are some explanations I should emphasise:

  • recma-mdx-interpolate works for only href/title parts of a markdown link and alt/src/title parts of a markdown image in "md" and "mdx" format, additionally text part of a link in "md" format.

  • The text part of a link (the children of an anchor) is already interpolated in MDX, so recma-mdx-interpolate does not touch it [{already.interpolated}](...) if the format is "mdx".

  • The curly braces in the alt of an image are removed during remark-mdx parsing in MDX (not in markdown format). So you need to use aferomentioned workaround if the format is "mdx".

  • If you are using a plugin (like rehype-image-toolkit) to convert image syntax to video/audio, then recma-mdx-interpolate also supports src/title of a <video>/<audio> elements; and src of a <source> element.

[!IMPORTANT] You should provide the value of identifiers in your integration (usually props in @mdx-js/mdx; scope in next-mdx-remote-client and next-mdx-remote etc).

The list of the tags and attributes that recma-mdx-interpolate processes

tags with "mdx" format with "md" format
<a> (link) href, title text (children), href, title
<img> alt*, src, title alt, src, title
<video> src, title src, title
<audio> src, title src, title
<source> src src

Note *: works with a workaround only.

recma-mdx-interpolate supports html in markdown

It supports html syntax besides markdown syntax for link and image in markdown contents. In MDX, the plugin doesn't touch these html syntax (actually MdxJsx elements) due to mdx parser handles interpolations already.

<a href={link.href} title={link.title}>{link.text}</a>
<img src={image.src} alt={image.alt} title={image.title}>

As you pay attention, there is no self-closing slash in <img> element above. Because there is no need self-closing slash for self-closable html elements in markdown (md) format, and of course in HTML5 standard. But, if you want to put a self-closing slash, put a space before it. Otherwise, the plugin infers that the self-closing slash belongs to the last attribute value, and produce unneccesary "/" in the value of the last attribute.

<img src={image.src} alt={image.alt} title={image.title}>  it is okey
<img src={image.src} alt={image.alt} title={image.title} /> it is okey
<img src={image.src} alt={image.alt} title={image.title}/> it is okey but the plugin infers "/" belongs the title.

Installation

This package is suitable for ESM only. In Node.js (version 18+), install with npm:

npm install recma-mdx-interpolate

or

yarn add recma-mdx-interpolate

Usage

Say we have the following file, example.mdx,

[{props.email}](mailto:{props.email} "{props.title}")

![{{alt}}]({src} "{title}")

And our module, example.js, looks as follows:

import { read } from "to-vfile";
import { compile } from "@mdx-js/mdx";
import recmaMdxInterpolate from "recma-mdx-interpolate";

main();

async function main() {
  const source = await read("example.mdx");

  const compiledSource = await compile(source, {
    recmaPlugins: [recmaMdxInterpolate],
  });

  return String(compiledSource);
}

Now, running node example.js produces the compiled source like below:

// ...
function _createMdxContent(props) {
  // ...
  return _jsxs(_Fragment, {
    children: [_jsx(_components.p, {
      children: _jsx(_components.a, {
-        href: "mailto:%7Bprops.email%7D",
+        href: `mailto:${props.email}`,
-        title: "{props.title}",
+        title: props.title,
        children: props.email
      })
    }), "\n", _jsx(_components.p, {
      children: _jsx(_components.img, {
-        src: "%7Bsrc%7D",
+        src: src,
-        alt: "{alt}",
+        alt: alt,
-        title: "{title}"
+        title: title,
      })
    })]
  });
}
// ...

This is roughly equivalent JSX with:

export default function MDXContent() {
  return (
    <p>
      <a href={`mailto:${props.email}`} title={props.title}>{props.email}</a>
    </p>
    <p>
      <img alt={alt} src={src} title={title} />
    </p>
  )
}

Options

All options are optional and have default values.

export type InterpolateOptions = {
  exclude?: Partial<Record<"a" | "img" | "video" | "audio" | "source", string | string[] | true>>
  disable?: boolean;
};

exclude

It is an object option to exlude some tags and attributes.

Default is empty object {} meaningly there is no excluded tags and attributes, all targets is going to be processed.

use(recmaMdxInterpolate, { exclude: {a: true} } as InterpolateOptions);

Now, the links (anchor <a>) will be excluded from processing.

use(recmaMdxInterpolate, { exclude: {img: ["src"]} } as InterpolateOptions);

Now, the src attribute of images will be excluded from processing.

disable

It is a boolean option to disable the plugin completely.

It is false by default.

It could be useful if you want the plugin NOT to work when the format is not "mdx".

use(recmaMdxInterpolate, { disable: format !== "mdx" } as InterpolateOptions);

Syntax tree

This plugin only modifies the ESAST (Ecma Script Abstract Syntax Tree) as explained.

Types

This package is fully typed with TypeScript. The plugin options is exported as InterpolateOptions.

Compatibility

This plugin works with unified version 6+. It is compatible with mdx version 3+.

Security

Use of recma-mdx-interpolate does not involve user content so there are no openings for cross-site scripting (XSS) attacks.

My Plugins

I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins.

My Remark Plugins

My Rehype Plugins

  • rehype-pre-language – Rehype plugin to add language information as a property to pre element
  • rehype-highlight-code-lines – Rehype plugin to add line numbers to code blocks and allow highlighting of desired code lines
  • rehype-code-meta – Rehype plugin to copy code.data.meta to code.properties.metastring
  • rehype-image-toolkit – Rehype plugin to enhance Markdown image syntax ![]() and Markdown/MDX media elements (<img>, <audio>, <video>) by auto-linking bracketed or parenthesized image URLs, wrapping them in <figure> with optional captions, unwrapping images/videos/audio from paragraph, parsing directives in title for styling and adding attributes, and dynamically converting images into <video> or <audio> elements based on file extension.

My Recma Plugins

  • 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 error
  • recma-mdx-change-props – Recma plugin to change the props parameter into the _props in the function _createMdxContent(props) {/* */} in the compiled source in order to be able to use {props.foo} like expressions. It is useful for the next-mdx-remote or next-mdx-remote-client users in nextjs applications.
  • recma-mdx-change-imports – Recma plugin to convert import declarations for assets and media with relative links into variable declarations with string URLs, enabling direct asset URL resolution in compiled MDX.
  • recma-mdx-import-media – Recma plugin to turn media relative paths into import declarations for both markdown and html syntax in MDX.
  • recma-mdx-import-react – Recma plugin to ensure getting React instance from the arguments and to make the runtime props {React, jsx, jsxs, jsxDev, Fragment} is available in the dynamically imported components in the compiled source of MDX.
  • recma-mdx-html-override – Recma plugin to allow selected raw HTML elements to be overridden via MDX components.
  • recma-mdx-interpolate – Recma plugin to enable interpolation of identifiers wrapped in curly braces within the alt, src, href, and title attributes of markdown link and image syntax in MDX.

License

MIT License © ipikuka

Package Sidebar

Install

npm i recma-mdx-interpolate

Weekly Downloads

3

Version

1.1.0

License

MIT

Unpacked Size

55.3 kB

Total Files

12

Last publish

Collaborators

  • talatkuyuk