unplugin / unplugin-vue-markdown

Compile Markdown to Vue component
MIT License
508 stars 27 forks source link

Markdown with Vue components in list item does not render link #7

Closed manniL closed 2 years ago

manniL commented 2 years ago

Describe the bug

When using a Vue component followed by a markdown link in a list item, the link is not rendered correctly. This worked with https://github.com/antfu/vite-plugin-md (Slidev v0.33.0 and before) For example: * <Counter /> [Example link](https://github.com/).

Related: https://github.com/slidevjs/slidev/issues/658

Manual reproduction:

  1. Add * <Counter /> [Test Link](https://github.com/) as last line in index.md in the examples folder. image

Reproduction

https://stackblitz.com/edit/slidev-nxyjmy?file=slides.md

System Info

System:
    OS: macOS 12.4
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 124.64 MB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.14.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 8.12.1 - ~/.npm/bin/npm
  Browsers:
    Brave Browser: 102.1.39.120
    Chrome: 103.0.5060.114
    Firefox: 84.0.1
    Firefox Developer Edition: 102.0
    Safari: 15.5
    Safari Technology Preview: 16.0

Used Package Manager

yarn

Validations

antfu commented 2 years ago

/cc @meteorlxy 👀

meteorlxy commented 2 years ago

Well... I'd say this is kind of as expected, because we are treating vue components as html block tags, instead of inline tags. That's say this plugin is stricter than vite-plugin-md.

See the differences between inline tags and block tags:

http://markdown-it.github.io/#md3=%7B%22source%22%3A%22%2A%20%3Cdiv%20%2F%3E%20%5BTest%20Link%5D%28https%3A%2F%2Fgithub.com%2F%29%5Cn%2A%20%3Cspan%20%2F%3E%20%5BTest%20Link%5D%28https%3A%2F%2Fgithub.com%2F%29%22%2C%22defaults%22%3A%7B%22html%22%3Atrue%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Afalse%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Atrue%2C%22typographer%22%3Atrue%2C%22_highlight%22%3Atrue%2C%22_strict%22%3Afalse%2C%22_view%22%3A%22src%22%7D%7D

meteorlxy commented 2 years ago

Maybe we could expose an API to allow users customizing which components should be treated as inline 🤔 ?

meteorlxy commented 2 years ago

This option should help with your case:

componentOptions: {
  inlineTags: ['mdi-github'],
}
manniL commented 2 years ago

Well... I'd say this is kind of as expected, because we are treating vue components as html block tags, instead of inline tags. That's say this plugin is stricter than vite-plugin-md.

Interesting insight, thanks! Could you briefly highlight why Vue components are treated as block tags and not as inline tags "anymore" (compared to vite-plugin-md)?

This option should help with your case:

componentOptions: {
  inlineTags: ['mdi-github'],
}

Thanks for providing a quick workaround for that issue 👍🏻 Do you think it'd be reasonable to have a flag that automatically treats all components as inline tags? So compat with vite-plugin-md is better (e.g. some of my slides are broken because of the switch in Slidev itself so a flag which could be set in Slidev might mitigate this issue)

meteorlxy commented 2 years ago

@manniL

The behavior of block tags would be better in a more common case - write component in multi-line:

<Foo
  class="foobar"
  title="baz"
/>

Check the differences here.

You'll notice that the inline one is wrapped with a <p> tag, which might not be expected for Vue components.

In fact, they are almost the same in most cases. What you reported is one of the edge cases that the behaviors differ. If the tag is not at the beginning of the list item, everything will work as expected:

* <div/> [Example link](https://github.com/)
* foobar<div/> [Example link](https://github.com/)

The reason is that a html block at the beginning of a line somehow "terminates" the parsing of the entire line.

It looks weird indeed, but markdown-it says it is a 100%-done-right CommonMark parser, so :expressionless:

manniL commented 2 years ago

Thanks for the detailed answer!

Would it make sense to differ between multi-line Vue components (block-level by default) and "inline" Vue components (inline-level by default) then? Sadly I'm not aware of the internals and if that's possible but this would kill two birds with one stone: No superfluous (or possibly even bug-introducing) <p> tag around multi-line components and compat behavior (for most cases).

meteorlxy commented 2 years ago

There could be a solution for that, but in fact I'm also not an expert of the markdown-it parsing flow so I didn't solve it yet 😢 .

meteorlxy commented 2 years ago

@manniL Good news, I think I made it at least for this case 😃

meteorlxy commented 2 years ago

I think the behavior is acceptable and only slightly violates the CommonMark spec.

Component tags would behave differently from block & inline tags:

block tags

Terminate the line:

<!-- input -->
<div /> [link](link)

<!-- output -->
<div /> [link](link)

inline tags

As normal inline content being wrapped with <p>:

<!-- input -->
<span /> [link](link)

<!-- output -->
<p><span /> <a href="link">link</a></p>

component tags (unknown tags)

Won't terminate the line and won't be wrapped with <p>:

<!-- input -->
<Counter /> [link](link)

<!-- output -->
<Counter /> <a href="link">link</a>
manniL commented 2 years ago

That looks pretty good 👍🏻 I also like the distinct differentiation between inline, block and component tags.