mirror of https://github.com/vuejs/vitepress
refactor: use markdown title plugin and remove parseHeader utils (#1235)
parent
71358ebce7
commit
19c0f43daa
@ -1,34 +0,0 @@
|
|||||||
import { test, expect } from 'vitest'
|
|
||||||
import { deeplyParseHeader } from 'node/utils/parseHeader'
|
|
||||||
|
|
||||||
test('deeplyParseHeader', () => {
|
|
||||||
const asserts: Record<string, string> = {
|
|
||||||
// remove tail html
|
|
||||||
'# `H1` <Comp></Comp>': '# H1',
|
|
||||||
'# *H1* <Comp/>': '# H1',
|
|
||||||
|
|
||||||
// reserve code-wrapped tail html
|
|
||||||
'# `H1` `<Comp></Comp>`': '# H1 <Comp></Comp>',
|
|
||||||
'# *H1* `<Comp/>`': '# H1 <Comp/>',
|
|
||||||
|
|
||||||
// remove leading html
|
|
||||||
'# <Comp></Comp> `H1`': '# H1',
|
|
||||||
'# <Comp/> *H1*': '# H1',
|
|
||||||
|
|
||||||
// reserve code-wrapped leading html
|
|
||||||
'# `<Comp></Comp>` `H1`': '# <Comp></Comp> H1',
|
|
||||||
'# `<Comp/>` *H1*': '# <Comp/> H1',
|
|
||||||
|
|
||||||
// remove middle html
|
|
||||||
'# `H1` <Comp></Comp> `H2`': '# H1 H2',
|
|
||||||
'# `H1` <Comp/> `H2`': '# H1 H2',
|
|
||||||
|
|
||||||
// reserve middle html
|
|
||||||
'# `H1` `<Comp></Comp>` `H2`': '# H1 <Comp></Comp> H2',
|
|
||||||
'# `H1` `<Comp/>` `H2`': '# H1 <Comp/> H2'
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(asserts).forEach((input) => {
|
|
||||||
expect(deeplyParseHeader(input)).toBe(asserts[input])
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,39 +0,0 @@
|
|||||||
import { describe, test, expect } from 'vitest'
|
|
||||||
import { parseHeader } from 'node/utils/parseHeader'
|
|
||||||
|
|
||||||
describe('parseHeader', () => {
|
|
||||||
test('should unescape html', () => {
|
|
||||||
const input = `<div :id="'app'">`
|
|
||||||
expect(parseHeader(input)).toBe(`<div :id="'app'">`)
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should remove markdown tokens correctly', () => {
|
|
||||||
const asserts: Record<string, string> = {
|
|
||||||
// vuepress #238
|
|
||||||
'[vue](vuejs.org)': 'vue',
|
|
||||||
'`vue`': 'vue',
|
|
||||||
'*vue*': 'vue',
|
|
||||||
'**vue**': 'vue',
|
|
||||||
'***vue***': 'vue',
|
|
||||||
_vue_: 'vue',
|
|
||||||
'\\_vue\\_': '_vue_',
|
|
||||||
'\\*vue\\*': '*vue*',
|
|
||||||
'\\!vue\\!': '!vue!',
|
|
||||||
|
|
||||||
// vuepress #2688
|
|
||||||
'[vue](vuejs.org) / [vue](vuejs.org)': 'vue / vue',
|
|
||||||
'[\\<ins>](vuejs.org)': '<ins>',
|
|
||||||
|
|
||||||
// vuepress #564 For multiple markdown tokens
|
|
||||||
'`a` and `b`': 'a and b',
|
|
||||||
'***bold and italic***': 'bold and italic',
|
|
||||||
'**bold** and *italic*': 'bold and italic',
|
|
||||||
|
|
||||||
// escaping \$
|
|
||||||
'\\$vue': '$vue'
|
|
||||||
}
|
|
||||||
Object.keys(asserts).forEach((input) => {
|
|
||||||
expect(parseHeader(input)).toBe(asserts[input])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,57 +0,0 @@
|
|||||||
import { test, expect } from 'vitest'
|
|
||||||
import { removeNonCodeWrappedHTML } from 'node/utils/parseHeader'
|
|
||||||
|
|
||||||
test('removeNonCodeWrappedHTML', () => {
|
|
||||||
const asserts: Record<string, string> = {
|
|
||||||
// Remove tail html
|
|
||||||
'# H1 <Comp></Comp>': '# H1 ',
|
|
||||||
'# H1<Comp></Comp>': '# H1',
|
|
||||||
'# H1 <Comp a="b"></Comp>': '# H1 ',
|
|
||||||
'# H1<Comp a="b"></Comp>': '# H1',
|
|
||||||
'# H1 <Comp/>': '# H1 ',
|
|
||||||
'# H1<Comp/>': '# H1',
|
|
||||||
'# H1 <Comp a="b"/>': '# H1 ',
|
|
||||||
'# H1<Comp a="b"/>': '# H1',
|
|
||||||
|
|
||||||
// Reserve code-wrapped tail html
|
|
||||||
'# H1 `<Comp></Comp>`': '# H1 `<Comp></Comp>`',
|
|
||||||
'# H1 `<Comp a="b"></Comp>`': '# H1 `<Comp a="b"></Comp>`',
|
|
||||||
'# H1 `<Comp/>`': '# H1 `<Comp/>`',
|
|
||||||
'# H1 `<Comp a="b"/>`': '# H1 `<Comp a="b"/>`',
|
|
||||||
|
|
||||||
// Remove leading html
|
|
||||||
'# <Comp></Comp> H1': '# H1',
|
|
||||||
'# <Comp></Comp>H1': '# H1',
|
|
||||||
'# <Comp a="b"></Comp> H1': '# H1',
|
|
||||||
'# <Comp a="b"></Comp>H1': '# H1',
|
|
||||||
'# <Comp/> H1': '# H1',
|
|
||||||
'# <Comp/>H1': '# H1',
|
|
||||||
'# <Comp a="b"/> H1': '# H1',
|
|
||||||
'# <Comp a="b"/>H1': '# H1',
|
|
||||||
|
|
||||||
// Reserve code-wrapped leading html
|
|
||||||
'# `<Comp></Comp>` H1': '# `<Comp></Comp>` H1',
|
|
||||||
'# `<Comp a="b"></Comp>` H1': '# `<Comp a="b"></Comp>` H1',
|
|
||||||
'# `<Comp/>` H1': '# `<Comp/>` H1',
|
|
||||||
'# `<Comp a="b"/>` H1': '# `<Comp a="b"/>` H1',
|
|
||||||
|
|
||||||
// Remove middle html
|
|
||||||
'# H1 <Comp></Comp> H2': '# H1 H2',
|
|
||||||
'# H1 <Comp a="b"></Comp> H2': '# H1 H2',
|
|
||||||
'# H1 <Comp/> H2': '# H1 H2',
|
|
||||||
'# H1 <Comp a="b"/> H2': '# H1 H2',
|
|
||||||
|
|
||||||
// Reserve code-wrapped middle html
|
|
||||||
'# H1 `<Comp></Comp>` H2': '# H1 `<Comp></Comp>` H2',
|
|
||||||
'# H1 `<Comp a="b"></Comp>` H2': '# H1 `<Comp a="b"></Comp>` H2',
|
|
||||||
'# H1 `<Comp/>` H2': '# H1 `<Comp/>` H2',
|
|
||||||
'# H1 `<Comp a="b"/>` H2': '# H1 `<Comp a="b"/>` H2',
|
|
||||||
|
|
||||||
// vuepress #2688
|
|
||||||
'# \\<ins>': '# \\<ins>'
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(asserts).forEach((input) => {
|
|
||||||
expect(removeNonCodeWrappedHTML(input)).toBe(asserts[input])
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,68 +0,0 @@
|
|||||||
// Since VuePress needs to extract the header from the markdown source
|
|
||||||
// file and display it in the sidebar or title (#238), this file simply
|
|
||||||
// removes some unnecessary elements to make header displays well at
|
|
||||||
// sidebar or title.
|
|
||||||
//
|
|
||||||
// But header's parsing in the markdown content is done by the markdown
|
|
||||||
// loader based on markdown-it. markdown-it parser will will always keep
|
|
||||||
// HTML in headers, so in VuePress, after being parsed by the markdown
|
|
||||||
// loader, the raw HTML in headers will finally be parsed by Vue-loader.
|
|
||||||
// so that we can write HTML/Vue in the header. One exception is the HTML
|
|
||||||
// wrapped by <code>(markdown token: '`') tag.
|
|
||||||
import emojiData from 'markdown-it-emoji/lib/data/full.json'
|
|
||||||
|
|
||||||
const parseEmojis = (str: string) => {
|
|
||||||
return str.replace(
|
|
||||||
/:(.+?):/g,
|
|
||||||
(placeholder, key) => (emojiData as any)[key] || placeholder
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const unescapeHtml = (html: string) =>
|
|
||||||
html
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, "'")
|
|
||||||
.replace(/:/g, ':')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
|
|
||||||
const removeMarkdownTokens = (str: string) =>
|
|
||||||
str
|
|
||||||
.replace(/(\[(.[^\]]+)\]\((.[^)]+)\))/g, '$2') // []()
|
|
||||||
.replace(/(`|\*{1,3}|_)(.*?[^\\])\1/g, '$2') // `{t}` | *{t}* | **{t}** | ***{t}*** | _{t}_
|
|
||||||
.replace(/(\\)(\*|_|`|\!|<|\$)/g, '$2') // remove escape char '\'
|
|
||||||
|
|
||||||
const removeCustomAnchor = (str: string) =>
|
|
||||||
str.replace(/\{#([a-zA-Z0-9\-_]+?)\}\s*$/, '') // {#custom-header}
|
|
||||||
|
|
||||||
const trim = (str: string) => str.trim()
|
|
||||||
|
|
||||||
// This method remove the raw HTML but reserve the HTML wrapped by `<code>`.
|
|
||||||
// e.g.
|
|
||||||
// Input: "<a> b", Output: "b"
|
|
||||||
// Input: "`<a>` b", Output: "`<a>` b"
|
|
||||||
export const removeNonCodeWrappedHTML = (str: string) => {
|
|
||||||
return String(str).replace(/(^|[^><`\\])<.*>([^><`]|$)/g, '$1$2')
|
|
||||||
}
|
|
||||||
|
|
||||||
const compose = (...processors: ((str: string) => string)[]) => {
|
|
||||||
if (processors.length === 0) return (input: string) => input
|
|
||||||
if (processors.length === 1) return processors[0]
|
|
||||||
return processors.reduce((prev, next) => {
|
|
||||||
return (str) => next(prev(str))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unescape html, parse emojis and remove some md tokens.
|
|
||||||
export const parseHeader = compose(
|
|
||||||
unescapeHtml,
|
|
||||||
parseEmojis,
|
|
||||||
removeCustomAnchor,
|
|
||||||
removeMarkdownTokens,
|
|
||||||
trim
|
|
||||||
)
|
|
||||||
|
|
||||||
// Also clean the html that isn't wrapped by code.
|
|
||||||
// Because we want to support using VUE components in headers.
|
|
||||||
// e.g. https://vuepress.vuejs.org/guide/using-vue.html#badge
|
|
||||||
export const deeplyParseHeader = compose(removeNonCodeWrappedHTML, parseHeader)
|
|
Loading…
Reference in new issue