vuejs / vitepress

Vite & Vue powered static site generator.
https://vitepress.dev
MIT License
12.03k stars 1.95k forks source link

Bug: cannot properly slugify badge with children #3511

Open shigma opened 5 months ago

shigma commented 5 months ago

Describe the bug

## Powered by Vite <Badge>HERE</Badge>

This header

  1. is not properly slugified (expected powered-by-vite, actual powered-by-vite-here),
  2. and cannot be configurated to be properly slugified.

Reproduction

https://stackblitz.com/edit/vite-hnczhc

or the official doc: https://vitepress.dev/reference/default-theme-badge#title-custom-element ← see this anchor

Expected behavior

  1. The header should be slugified without badge children (if I use props instead, it is slugified as expected, but I cannot do so).
  2. I use the following config to make it look better:
  markdown: {
    anchor: {
      slugify: (str) => slugify(str.replace(/ *<badge.+/i, '')),
    },
  },

It is working alright in the past, but not working for now. Actually the str received is Powered by Vite HERE, without tag information.

I believe this is a regression from vitepress or mdit-vue.

System Info

N/A

Additional context

Why don't you use props instead of children?

Because this is an i18n doc site. The children can be translated via Crowdin, but props cannot.

With such inconsistencies, at least I don't think the designers of the badge component fully considered the i18n experience. Vitepress is an excellent project and I hope it gets better.

Validations

brc-dd commented 5 months ago

It is working alright in the past, but not working for now.

This probably never supported badges with slots. For example - https://stackblitz.com/edit/vite-2j5pug?file=package.json (we added badge in alpha.27 which was using mdit-vue@0.11.0)

We can probably fix that though.

PS: I think it might have worked with very old vitepress versions when markdown-it-anchor didn't had this - https://github.com/valeriangalliat/markdown-it-anchor/blob/69cbf727367c6b10a553a8549790a6d6df917342/index.js#L56

You can do something like this in meantime:

  markdown: {
    anchor: {
      getTokensText(tokens) {
        let text = ''
        for (const t of tokens) {
          if (t.type === 'text' || t.type === 'code_inline') text += t.content
          if (t.type === 'html_inline' && /<badge/i.test(t.content)) return text
        }
        return text
      }
    }
  }
shigma commented 5 months ago

The code snippet you gave works perfectly, thanks a lot!

We can probably fix that though.

Maybe the behaviors between badge with props and badge with children should be aligned, but other elements shouldn't necessarily.

From your perspective, what is the expected behavior here?

brc-dd commented 5 months ago

Maybe the behaviors between badge with props and badge with children should be aligned

Actually, it's a bit more complicated. Let me explain:

## foo <badge>bar</badge>

is parsed by markdown-it to 

text - foo
html_inline - <badge>
text - bar
html_inline - </badge>

## foo <badge text="bar" />

is parsed to

text - foo
html_inline - <badge text="bar" />

This behavior is unrelated to vitepress - https://markdown-it.github.io/#md3=%7B%22source%22%3A%22%23%23%20foo%20%3Cbadge%3Ebar%3C%2Fbadge%3E%22%2C%22defaults%22%3A%7B%22html%22%3Afalse%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Afalse%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Afalse%2C%22typographer%22%3Afalse%2C%22_highlight%22%3Afalse%2C%22_strict%22%3Atrue%2C%22_view%22%3A%22debug%22%7D%7D

And markdown-it-anchor keeps the content of only text and code_inline nodes - https://github.com/valeriangalliat/markdown-it-anchor/blob/69cbf727367c6b10a553a8549790a6d6df917342/index.js#L7

Your slugify is redundant here, as the badge will already be stripped if you're using props, and if you're using slots it won't work because it will receive just "foo bar" as argument. So slugify doesn't do anything either way and is not required.

what is the expected behavior here?

From the DX perspective, we can have some opinionated default for getTokensText and remove the badges and content inside them (as we also remove badges from outline, etc.)