Skip to content
CONSTRUCTING

Elements Transformation v2.9.0

Word count
708 words
Reading time
5 minutes

This is a low-level plugin that is intended to be used by advanced users who want to transform the markdown-it tokens to perform tasks like replacing elements, adding attributes, etc.

Before you start

This plugin primarily is a helper utility markdown-it plugin for Inline Links Previewing to help to transform all the <a> elements to <VPNolebaseInlineLinkPreview> components.

When using this plugin directly, you may encounter errors like Invalid tag, or Element missing end tag errors when integrating with VitePress or Vue markdown plugins after transforming tokens and elements, or other markdown-it plugins that are not compatible with the transformed elements. Please use it when you understand how the Token API of markdown-it works.

Installation

Install @nolebase/markdown-it-bi-element-transform to your project dependencies by running the following command:

shell
ni @nolebase/markdown-it-bi-element-transform -D
shell
pnpm add @nolebase/markdown-it-bi-element-transform -D
shell
npm install @nolebase/markdown-it-bi-element-transform -D
shell
yarn add @nolebase/markdown-it-bi-element-transform -D

Usage

Suggestions to developers

When learning, debugging, we suggest including vite-plugin-inspect in your project. It allows you to inspect the intermediate state of Vite plugins, Markdown transformations. After installing, you can visit to inspect the modules and transformation stack of your project.

Check out install instructions in the vite-plugin-inspect docs.

Screenshot of vite-plugin-inspect

Change the tag of the elements

This is the living example code where we use it as references:

ts
import MarkdownIt from 'markdown-it'
let markdownIt: MarkdownIt = null as unknown as MarkdownIt
// ---cut---
import { ElementTransform } from '@nolebase/markdown-it-element-transform'

markdownIt.use(ElementTransform, (() => {
  // Define variables inside the closure function to share the state between multiple tokens
  // Here we need a variable to track whether the next token needs to be transformed or not because we need to modify two tokens (link_open, link_close)
  let transformNextLinkCloseToken = false

  return {
    transform(token) {
      switch (token.type) {
        case 'link_open':
          // You can have some conditions here to skip the transformation
          //
          // Skip the transformation if the token is a header anchor
          // since usually the header anchor is not quite the same as the normal link
          if (token.attrGet('class') !== 'header-anchor') {
            // Modify the tag of the token
            token.tag = 'YourLinkComponentName'
            // Set the flag to transform the next link_close token
            transformNextLinkCloseToken = true
          }
          break
        case 'link_close':
          // Transform the token if the flag is set
          if (transformNextLinkCloseToken) {
            // Modify the tag of the token
            token.tag = 'YourLinkComponentName'
            // Reset the flag
            transformNextLinkCloseToken = false
          }

          break
      }
    },
  }
})())

Use it to add more attributes to the elements

There are many use cases where you want to add more attributes to the elements.

For example:

  1. Add target="_blank" and rel="noopener noreferrer" to all the <a> elements.
  2. Add inline styles to all the <span> elements.
  3. Add more classes to the elements.
ts
import MarkdownIt from 'markdown-it'
let markdownIt: MarkdownIt = null as unknown as MarkdownIt
// ---cut---
import { ElementTransform } from '@nolebase/markdown-it-element-transform'

markdownIt.use(ElementTransform, (() => {
  return {
    transform(token) {
      if (token.type === 'link_open') {
        token.attrSet('target', '_blank')
        token.attrSet('rel', 'noopener noreferrer')
      }
    },
  }
})())

Add inline styles to the elements

ts
import MarkdownIt from 'markdown-it'
let markdownIt: MarkdownIt = null as unknown as MarkdownIt
// ---cut---
import { ElementTransform } from '@nolebase/markdown-it-element-transform'

markdownIt.use(ElementTransform, (() => {
  return {
    transform(token) {
      if (token.type === 'span') {
        token.attrSet('style', 'color: red;')
      }
    },
  }
})())

Add more classes to the elements

ts
import MarkdownIt from 'markdown-it'
let markdownIt: MarkdownIt = null as unknown as MarkdownIt
// ---cut---
import { ElementTransform } from '@nolebase/markdown-it-element-transform'

markdownIt.use(ElementTransform, (() => {
  return {
    transform(token) {
      if (token.type === 'span') {
        const existingClasses = token.attrGet('class') || ''
        token.attrSet('class', existingClasses + ' my-custom-class')
      }
    },
  }
})())

Alternatives?

jeffbski/markdown-it-modify-token

Yes, this plugin does everything literally the same as jeffbski/markdown-it-modify-token. The reason why we have our own implementation is because we didn't know the existence of the plugin until we have implemented it.

Feel free to use the alternative if you want to. 😋

Contributors

Changelog