|
1 | 1 | # Elements Transformation <Badge type="tip" text="v1.28.0" /> |
2 | 2 |
|
3 | | -::: warning 🚧 Constructing |
4 | | -Nice to meet you! But sorry, this page is still under construction. If you don’t find the information you are interested in, you can first find the content you are interested in in the navigation in the sidebar to start reading. |
| 3 | +This is a low-level plugin that is intended to be used by advanced users who want to transform the `markdown-it` [tokens](https://markdown-it.github.io/markdown-it/#Token) to perform tasks like replacing elements, adding attributes, etc. |
| 4 | + |
| 5 | +::: warning Before you start |
| 6 | + |
| 7 | +This plugin primarily is a helper utility markdown-it plugin for [Inline Links Previewing](/pages/en/integrations/vitepress-plugin-inline-link-preview/) to help to transform all the `<a>` elements to `<NolebaseInlineLinkPreview>` components. |
| 8 | + |
| 9 | +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](https://markdown-it.github.io/markdown-it/#Token) and elements, |
| 10 | +or other markdown-it plugins that are not compatible with the transformed elements. Please use it when you understand how the [Token API](https://markdown-it.github.io/markdown-it/#Token) of `markdown-it` works. |
| 11 | + |
| 12 | +::: |
| 13 | + |
| 14 | +## Installation |
| 15 | + |
| 16 | +Install `@nolebase/markdown-it-bi-element-transform` to your project dependencies by running the following command: |
| 17 | + |
| 18 | +::: code-group |
| 19 | + |
| 20 | +```shell [@antfu/ni] |
| 21 | +ni @nolebase/markdown-it-bi-element-transform -D |
| 22 | +``` |
| 23 | + |
| 24 | +```shell [pnpm] |
| 25 | +pnpm add @nolebase/markdown-it-bi-element-transform -D |
| 26 | +``` |
| 27 | + |
| 28 | +```shell [npm] |
| 29 | +npm install @nolebase/markdown-it-bi-element-transform -D |
| 30 | +``` |
| 31 | + |
| 32 | +```shell [yarn] |
| 33 | +yarn add @nolebase/markdown-it-bi-element-transform -D |
| 34 | +``` |
| 35 | + |
5 | 36 | ::: |
| 37 | + |
| 38 | + |
| 39 | +## Usage |
| 40 | + |
| 41 | +::: tip Suggestions to developers |
| 42 | + |
| 43 | +When learning, debugging, we suggest including [`vite-plugin-inspect`](https://github.com/antfu/vite-plugin-inspect) in your project. It allows you to inspect the intermediate state of Vite plugins, Markdown transformations. After installing, you can visit [](http://localhost:5173/__inspect/) to inspect the modules and transformation stack of your project. |
| 44 | + |
| 45 | +Check out install instructions in the [`vite-plugin-inspect` docs](https://github.com/antfu/vite-plugin-inspect). |
| 46 | + |
| 47 | +<picture> |
| 48 | + <source srcset="./assets/vite-plugin-inspect-screenshot-day-theme.png" media="(prefers-color-scheme: light)"> |
| 49 | + <source srcset="./assets/vite-plugin-inspect-screenshot-night-theme.png" media="(prefers-color-scheme: dark)"> |
| 50 | + <img src="./assets//vite-plugin-inspect-screenshot-day-theme.png" alt="Screenshot of vite-plugin-inspect" /> |
| 51 | +</picture> |
| 52 | + |
| 53 | +::: |
| 54 | + |
| 55 | +### Use with [Inline Links Previewing](/pages/en/integrations/vitepress-plugin-inline-link-preview/) |
| 56 | + |
| 57 | +Use this plugin with [Inline Links Previewing](/pages/en/integrations/vitepress-plugin-inline-link-preview/) to replace all the `<a>` elements to `<NolebaseInlineLinkPreview>` components to enable the abilities to preview the links. |
| 58 | + |
| 59 | +::: danger Implementation may change in the future for [Inline Links Previewing](/pages/en/integrations/vitepress-plugin-inline-link-preview/) |
| 60 | + |
| 61 | +Such usage is a temporary workaround due to the fact that we haven't had time to refactor [Inline Links Previewing](/pages/en/integrations/vitepress-plugin-inline-link-preview/) plugin to listen the mouse hovering event of all the `<a>` elements as singleton instance across all pages for both better accessibility and compatibilities (since it replaces all the `<a>` elements and may break the markdown processing pipelines for advanced users). |
| 62 | + |
| 63 | +::: |
| 64 | + |
| 65 | +This is the living example code where we use it as references: |
| 66 | + |
| 67 | +```ts twoslash |
| 68 | +import MarkdownIt from 'markdown-it' |
| 69 | +let markdownIt: MarkdownIt = null as unknown as MarkdownIt |
| 70 | +// ---cut--- |
| 71 | +import { ElementTransform } from '@nolebase/markdown-it-element-transform' |
| 72 | + |
| 73 | +markdownIt.use(ElementTransform, (() => { |
| 74 | + // Define variables inside the closure function to share the state between multiple tokens |
| 75 | + // 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) |
| 76 | + let transformNextLinkCloseToken = false |
| 77 | + |
| 78 | + return { |
| 79 | + transform(token) { |
| 80 | + switch (token.type) { |
| 81 | + case 'link_open': |
| 82 | + // Skip the transformation if the token is a header anchor |
| 83 | + // because the header anchor doesn't need to be previewed |
| 84 | + if (token.attrGet('class') !== 'header-anchor') { |
| 85 | + // Modify the tag of the token |
| 86 | + token.tag = 'NolebaseInlineLinkPreview' |
| 87 | + // Set the flag to transform the next link_close token |
| 88 | + transformNextLinkCloseToken = true |
| 89 | + } |
| 90 | + break |
| 91 | + case 'link_close': |
| 92 | + // Transform the token if the flag is set |
| 93 | + if (transformNextLinkCloseToken) { |
| 94 | + // Modify the tag of the token |
| 95 | + token.tag = 'NolebaseInlineLinkPreview' |
| 96 | + // Reset the flag |
| 97 | + transformNextLinkCloseToken = false |
| 98 | + } |
| 99 | + |
| 100 | + break |
| 101 | + } |
| 102 | + }, |
| 103 | + } |
| 104 | +})()) |
| 105 | +``` |
| 106 | + |
| 107 | +### Use it to add more attributes to the elements |
| 108 | + |
| 109 | +There are many use cases where you want to add more attributes to the elements. |
| 110 | + |
| 111 | +For example: |
| 112 | + |
| 113 | +1. Add `target="_blank"` and `rel="noopener noreferrer"` to all the `<a>` elements. |
| 114 | +2. Add inline styles to all the `<span>` elements. |
| 115 | +3. Add more classes to the elements. |
| 116 | + |
| 117 | +#### Add `target="_blank"` and `rel="noopener noreferrer"` to links |
| 118 | + |
| 119 | +```ts twoslash |
| 120 | +import MarkdownIt from 'markdown-it' |
| 121 | +let markdownIt: MarkdownIt = null as unknown as MarkdownIt |
| 122 | +// ---cut--- |
| 123 | +import { ElementTransform } from '@nolebase/markdown-it-element-transform' |
| 124 | + |
| 125 | +markdownIt.use(ElementTransform, (() => { |
| 126 | + return { |
| 127 | + transform(token) { |
| 128 | + if (token.type === 'link_open') { |
| 129 | + token.attrSet('target', '_blank') |
| 130 | + token.attrSet('rel', 'noopener noreferrer') |
| 131 | + } |
| 132 | + }, |
| 133 | + } |
| 134 | +})()) |
| 135 | +``` |
| 136 | + |
| 137 | +#### Add inline styles to the elements |
| 138 | + |
| 139 | +```ts twoslash |
| 140 | +import MarkdownIt from 'markdown-it' |
| 141 | +let markdownIt: MarkdownIt = null as unknown as MarkdownIt |
| 142 | +// ---cut--- |
| 143 | +import { ElementTransform } from '@nolebase/markdown-it-element-transform' |
| 144 | + |
| 145 | +markdownIt.use(ElementTransform, (() => { |
| 146 | + return { |
| 147 | + transform(token) { |
| 148 | + if (token.type === 'span') { |
| 149 | + token.attrSet('style', 'color: red;') |
| 150 | + } |
| 151 | + }, |
| 152 | + } |
| 153 | +})()) |
| 154 | +``` |
| 155 | + |
| 156 | +#### Add more classes to the elements |
| 157 | + |
| 158 | +```ts twoslash |
| 159 | +import MarkdownIt from 'markdown-it' |
| 160 | +let markdownIt: MarkdownIt = null as unknown as MarkdownIt |
| 161 | +// ---cut--- |
| 162 | +import { ElementTransform } from '@nolebase/markdown-it-element-transform' |
| 163 | + |
| 164 | +markdownIt.use(ElementTransform, (() => { |
| 165 | + return { |
| 166 | + transform(token) { |
| 167 | + if (token.type === 'span') { |
| 168 | + const existingClasses = token.attrGet('class') || '' |
| 169 | + token.attrSet('class', existingClasses + ' my-custom-class') |
| 170 | + } |
| 171 | + }, |
| 172 | + } |
| 173 | +})()) |
| 174 | +``` |
| 175 | + |
| 176 | +## Alternatives? |
| 177 | + |
| 178 | +### [jeffbski/markdown-it-modify-token](https://github.com/jeffbski/markdown-it-modify-token) |
| 179 | + |
| 180 | +Yes, this plugin does everything literally the same as [jeffbski/markdown-it-modify-token](https://github.com/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. |
| 181 | + |
| 182 | +Feel free to use the alternative if you want to. 😋 |
0 commit comments