remarkjs/react-markdown

feature-request: Support passing hProperties to renderers

bj00rn opened this issue ยท 10 comments

I was hoping to be able to use the remark-attr plugin in order to pass attributes from markdown.

remark-attr plugin supports declaring attributes on elements like so:

### This is a title
{id="maintitle" style="color:red;"}

These properties are parsed correctly by the plugin into node.data.hProperties However, properties are not passed on by react-markdown to the renderers.

ast-to-react.js

image

Passing on hProperties would be nice, but maybe it has some security implications.

About the security aspect. remark-attr handle a lot of that part. It uses a whitelist approach.

By default, only standard attributes not event based(onclick, on...) are allowed.

In the meantime, if you like hacks (and who doesn't?) I have this workaround in my project:

  • pass rawSourcePos={true} to ReactMarkdown
  • create a map imageNodes to store references to your image nodes (see code below)
  • pass a function to allowNodes that saves references to image nodes
  • have a custom image renderer which looks up the corresponding node in imageNodes (the node contains data.hProperties)
function MyMarkdown(props) {
  const imageNodes = new Map();
  const nodeKey = position => JSON.stringify(position.start); // or use your own hash function

  function allowNodes(node) {
    if (node.type === "image") imageNodes.set(nodeKey(node.position), node);
    return true;
  }

  const renderers = {
    image: ({ sourcePosition, alt, src, title }) => {
      const node = imageNodes.get(nodeKey(sourcePosition));
      return (
        <img alt={alt} src={src} title={title} {...node.data.hProperties} />
      );
    }
  };

  return (
    <ReactMarkdown
      {...props}
      allowNodes={allowNodes}
      renderers={renderers}
      rawSourcePos
    />
  );
}

You don't need to clean up imageNodes because it will be recreated and disposed on every render.

Yes, remark-attr support would be great!

In the meantime, if you like hacks (and who doesn't?) I have this workaround in my project:
...

I used this code to resize images in a project I'm making. I did a short writeup of it on my blog here:

https://jacobwicks.github.io/2020/06/19/rendering-markdown-and-resizing-images-with-react-markdown.html

Thanks for the example!

this will be resolved by #428 which leverages remark-rehype which handles hProperties

This should be solved by landing GH-563 today, which will soon be released in v6.0.0!

Is there a trick to using this (other than passing remark-attr to the remarkPlugins array? The following isn't working for me:

import React from 'react'
import ReactMarkdown from 'react-markdown'
import attributes from 'remark-attr'
import behead from 'remark-behead'
import slug from 'remark-slug'

const REMARK_PLUGINS = [
  [behead, { depth: 1 }],
  [slug],
  [attributes],
]

const Markdown = ({ children }) => {
	<ReactMarkdown
      remarkPlugins={REMARK_PLUGINS}
    >
        {children}
      </ReactMarkdown>
}

But this results in the following error:

Error: Missing parser to attach `remark-attr` [link] (to)

This looks like it's caused by this check in remark-attr, which calls the following function:

function isRemarkParser(parser) {
  return Boolean(
    parser &&
    parser.prototype &&
    parser.prototype.inlineTokenizers &&
    parser.prototype.inlineTokenizers.link &&
    parser.prototype.inlineTokenizers.link.locator,
  );
}

(This might be an issue with the remark-attr implementation, so let me know if this is the case and I'll open an issue there)

remark-attr has not updated to the latest parser. See status indications for plugins in the list of plugins. And the issue tracker on that project.

@wooorm thanks so much for the quick response, will keep an eye on that page!

I plan to update remark-attr before September. No guarantee.
As it's need for my use case.

If someone wants deadline or support. We may find a monetary arrangement. Otherwise,I think @wooorm has pretty good alternatives. :)