Open wooorm opened 4 years ago
Ok, so I took the night to study the problem.
If I understand well, this plugin is obsolete. The parsing is now done in micromark and than I can't rewrite the tokenizers of remark-parse
.
I wonder what to do. How should I support such a syntax ? Basically everything is outdated and I must rewrite a plugin from scratch and first study micromark. It's a lot of hard work.
A hint on how to do it well will be very welcome. I guess that you don't remember what that plugin does and how it works. Here a little summary :
This plugin extends the tokenizers of remark-parse to support syntax like that : *Hello !*{ style="red"}
. A new tokenizer is created, it call the old tokenizer and them parse the { style="red"}
syntax; and modify the result of the old tokenizer consequently (adding attributes).
So now, they is no more tokernizer to extend. Should I extend the state machine of micromark (SyntaxExtension ?) or do it in the unified/remark ecosystem (maybe hard or near to impossible due to edge-cases as *Hello !*{ comment="I _like_ the work on remark"}
where the AST will be complex ) ?
Ok, thinking about it made me realize that I only one main question. I think, i have no chose but to extend micromark. Do you think it's possible ? Is there any doc available about it or I will have to study the source code ?
Thank for improving the remark ecosystem and in advance to any help provided.
For generic directives, I wrote an attribute parser, that support #id.class boolean key=value or="quoted"
attributes: https://github.com/micromark/micromark-extension-directive/blob/main/lib/factory-attributes.js. That’s a lot of what you’d need to write.
Architecturally, there is a) a tokenizing part, where you parse {attributes}
in micromark, and b) a tree building part, where you turn those tokens into mdast—for directives, that’s mdast-util-directive/from-markdown.js
. Last, the remark part bundles it all so that users don’t need to know about everything.
I like building things in separate projects. But you don’t have to: it can all live in this project. The reasons why I split things up, are a) just nice coding for me, b) not everyone needs a syntax tree, so I also have micromark html extensions, c) in the future there will be a CST, not sure how or when that would be, but mdast wouldn’t be the only tree format.
For the tokenizing part, I think you’d be looking for {
, then try and parse attributes, then find a }
, and you‘re done! With one exception, which I’ll describe later. The second thing is in the adapter, where you‘re taking tokens and building trees, to take your attribute tokens and add them to the preceding link or heading. mdast-util-directive
has attributes
which aren’t hast, but the remark-directive
readme shows an example of making hProperties out of ’em.
The complex part for attrs is that they’re added to other constructs. You can access the events
on micromark, so that you can check to parse attributes or not based on whether there’s a link or image right before it. Links and images are made up of a start (![
, [
), and an end (](asd)
, ][asd]
, ][]
, or ]
), which is done here: https://github.com/micromark/micromark/blob/ed4d78e4e2a799858d2c2c4f87b09163f1eae9da/lib/tokenize/label-end.js#L164. Those and autolinks, code spans, footnote calls, inline footnotes, are pretty easy. But emphasis, strong, and strikethrough are harder because those sequences can open and close, which is figured out at the end.
For the flow part (blocks), I see that you have two different ways: setext headings attributes on their own line, ATX headings inline. I would suggest doing only the setext heading way: a “block” attribute starts at a line, cannot include line endings, and ends at a line ending. Then, it’s attached to the preceding block, whether it’s a paragraph, a heading (ATX or setext), thematic breaks, code (fenced or indented). Containers (lists and block quotes) would be more complex, but as you’re not handling them yet, we can skip that for now.
Alright, already a wall of text, so I’ll quit now. That should give you some ideas on how to go about it.
An alternative is: The downside of attributes is that they don’t work in other markdown parsers. More markdown parsers are moving to generic directives. You could push folks to instead switch to a directive based approach? Such as the example in the remark-directive
readme.
What's the current status? Is there a new remark plugin on the horizon that will provide the functionality of this one with the new micromark parser?
No progress. I don't have any need for it, so I focus on other topics.
Any contribution will be welcomed.
For anyone else who runs into this, I got this working with a custom component for my scenario (I needed call-to-action button-links). All buttons that have \ as the first-child [**text here**](... "custom:attribute title text")
are rendered as buttons and their title
text is parsed for custom attribute strings. Highly limited, but worked for me!
This pipeline relies on react-markdown
.
// typescript component rendering
import React, { ReactElement } from "react";
import ReactMarkdown from "react-markdown";
import { Components, ReactBaseProps, ReactMarkdownProps } from "react-markdown/src/ast-to-react";
import remarkGfm from "remark-gfm";
import { first, fromPairs } from "lodash";
import { Button } from "@material-ui/core";
const components: Components = {
a: ({
node,
children,
href,
title,
...props
}: ReactBaseProps & ReactMarkdownProps & { href: string; title: string }) => {
// if the first child of the button is a bold element, make this a CTA button
if ((first(children) as ReactElement)?.type === "strong") {
const splitTitle = title.split(' ');
const titleAttrEntries = splitTitle.filter(t => t.includes(':'));
const remainingTitleEntries = splitTitle.filter(t => !t.includes(':'));
const finalTitle = remainingTitleEntries.join(' ');
const titleAttrPairs = titleAttrEntries.map(te => te.split(':'))
const titleAttrs = fromPairs(titleAttrPairs);
return (
<Button variant="contained" href={href} title={finalTitle} {...props} {...titleAttrs}>
{children}
</Button>
);
}
return <a href={href} title={title} children={children} {...props}></a>;
},
};
export function renderMarkdown(markdown: string) {
return (
<ReactMarkdown
plugins={[remarkGfm]}
children={markdown}
components={components}/>
);
}
<!-- markdown -->
[Regular Link](/where_to_go/)
[**CTA Button Link**](/where_to_go/ "color:primary any:attribute things without colons will be treated as the title")
output
It looks like this may be a suitable replacement, as well https://github.com/remarkjs/remark-directive
Directive should have been the way to go since the CommonMark compatibility, but directive never really make it way to the CommonMark standard.
I wonder if it's used anywhere. But in principle, it's nice, maybe a little complex but still nice.
It's also difficult to implement to remark but that not a real argument because attributes-style (like remark-attr) become slightly more complicated to implement with µmark.
PS: If you plan to use remark-directive take a look at https://github.com/micromark/micromark-extension-directive.
I wonder if it's used anywhere. But in principle, it's nice, maybe a little complex but still nice.
It’s getting some traction: a growing number of questions about it and users of it!
Made a simple plugin from scratch that works well with remark@next
:
https://gist.github.com/cah4a/9b75c531540e2891599453863dd24881
Don't know how well it is aligned with the plugins ecosystem. Patching hProperties
seems not a very delicate solution, but it works.
If somebody has the power to make a package out of it, feel free to grab the source.
Don't know how well it is aligned with the plugins ecosystem. Patching
hProperties
seems not a very delicate solution, but it works.If somebody has the power to make a package out of it, feel free to grab the source.
Is this code still working? Has anyone made a package out of it yet? That would be much appreciated!
Hi!
remark is switching to a new parser (and compiler) internally (micromark,
remarkjs/remark#536
), which will break this plugin. Keep an eye out for remark-footnotes, remark-frontmatter, remark-gfm, remark-math, and other syntax plugins similar to this one, which could serve as inspiration for this plugin. Typically, these syntax plugins will be small wrapper code around micromark and mdast extensions btw. Also: feel free to ask me questions!