htmlmarkdownremarkjs

Markdown to HTML using unifiedjs


I am using unified(), remark-directive and remark-parse to convert Markdown to HTML and I can successfully convert the most of the Markdown but the markdown format below is being ignored. I am aware of the following markdown pattern [Anchor Link Text](https://test.com) for the anchor tag which works fine. Anyone has come across this pattern? I am looking any unified plugins that can help with this without any luck.

:a[Anchor Link Text]{href='https://www.testurl.com/movies/news/the-url-to-test'}


Solution

  • The syntax looks like the Generic Directives Proposal.

    In the remark-directive documentation under API, it notes that the plugin parses the directives for the syntax tree (like containerDirective, leafDirective, textDirective, etc.). The plugin doesn't handle implementing/transforming the directives and a developer needs to implement their own plugin to handle the directives. Because directives are generic and not standardized, there likely isn't a premade plugin for each project's unique implementation.

    The custom plugin myRemarkPlugin example in the remark-directive Use section may conveniently cover your use case.

    The function myRemarkPlugin creates a tag with the same name as the :a and properties from {}. In the desired case, replacing an :a with an HTML tag a and the directive key/values {href="#"} are the HTML tag props fits that implementation.

    It will transform any ::: or :: or : directives to an HTML tag of the same name, so tweak the logic for your requirements. If there are other directive replacement patterns, those need to be handled with their own logic.

    Use example pulled from remark-directive GitHub:

    // Register `hName`, `hProperties` types, used when turning markdown to HTML:
    /// <reference types="mdast-util-to-hast" />
    // Register directive nodes in mdast:
    /// <reference types="mdast-util-directive" />
    
    import {h} from 'hastscript'
    import rehypeFormat from 'rehype-format'
    import rehypeStringify from 'rehype-stringify'
    import remarkDirective from 'remark-directive'
    import remarkParse from 'remark-parse'
    import remarkRehype from 'remark-rehype'
    import {read} from 'to-vfile'
    import {unified} from 'unified'
    import {visit} from 'unist-util-visit'
    
    const file = await unified()
      .use(remarkParse)
      .use(remarkDirective)
      .use(myRemarkPlugin) // Custom plugin defined below
      .use(remarkRehype)
      .use(rehypeFormat)
      .use(rehypeStringify)
      .process(await read('example.md'))
    
    // Custom plugin
    function myRemarkPlugin() {
      /**
       * @param {import('mdast').Root} tree
       *   Tree.
       * @returns {undefined}
       *   Nothing.
       */
      return function (tree) {
        visit(tree, function (node) {
          if (
            node.type === 'containerDirective' ||  // ':::' 
            node.type === 'leafDirective' || // '::' 
            node.type === 'textDirective' // ':' 
          ) {
            const data = node.data || (node.data = {})
            const hast = h(node.name, node.attributes || {})
    
            data.hName = hast.tagName
            data.hProperties = hast.properties
          }
        })
      }
    }
    

    The implementation with the custom plugin transforms

    :::main{#readme}
    
    Lorem:br
    ipsum.
    
    ::hr{.red}
    
    A :i[lovely] language know as :abbr[HTML]{title="HyperText Markup Language"}.
    
    :::
    

    into

    <main id="readme">
      <p>Lorem<br>ipsum.</p>
      <hr class="red">
      <p>A <i>lovely</i> language know as <abbr title="HyperText Markup Language">HTML</abbr>.</p>
    </main>
    

    And should transform

    :a[Anchor Link Text]{href='https://www.testurl.com/movies/news/the-url-to-test'}
    

    into

    <a href="https://www.testurl.com/movies/news/the-url-to-test">Anchor Link Test</a>