From c3696d25ef36a493100bda861216bc47f6f14f38 Mon Sep 17 00:00:00 2001
From: ULIVZ <472590061@qq.com>
Date: Sun, 3 Jun 2018 04:38:33 +0800
Subject: [PATCH] feat: headers badge (#540)
---
docs/guide/markdown.md | 2 +-
lib/app/app.js | 2 ++
lib/default-theme/Badge.vue | 30 +++++++++++++++++++++++++++++
lib/default-theme/styles/theme.styl | 1 +
lib/default-theme/styles/toc.styl | 3 +++
lib/markdown/index.js | 5 +++--
lib/util/index.js | 10 +++++-----
lib/util/parseHeaders.js | 24 +++++++++++++++--------
lib/util/shared.js | 7 +++++++
9 files changed, 68 insertions(+), 16 deletions(-)
create mode 100644 lib/default-theme/Badge.vue
create mode 100644 lib/default-theme/styles/toc.styl
create mode 100644 lib/util/shared.js
diff --git a/docs/guide/markdown.md b/docs/guide/markdown.md
index 5f9548e15f..71d03c7dc4 100644
--- a/docs/guide/markdown.md
+++ b/docs/guide/markdown.md
@@ -224,7 +224,7 @@ export default {
}
```
-## Import Code Snippets
+## Import Code Snippets
You can import code snippets from existing files via following syntax:
diff --git a/lib/app/app.js b/lib/app/app.js
index d8da7dccc0..1f64553e8a 100644
--- a/lib/app/app.js
+++ b/lib/app/app.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
import Router from 'vue-router'
import Content from './Content'
import OutboundLink from '../default-theme/OutboundLink.vue'
+import Badge from '../default-theme/Badge.vue'
import ClientOnly from './ClientOnly'
import dataMixin from './dataMixin'
import store from './store'
@@ -30,6 +31,7 @@ Vue.mixin(dataMixin(siteData))
// component for rendering markdown content and setting title etc.
Vue.component('Content', Content)
Vue.component('OutboundLink', OutboundLink)
+Vue.component('Badge', Badge)
// component for client-only content
Vue.component('ClientOnly', ClientOnly)
diff --git a/lib/default-theme/Badge.vue b/lib/default-theme/Badge.vue
new file mode 100644
index 0000000000..1a41d7e772
--- /dev/null
+++ b/lib/default-theme/Badge.vue
@@ -0,0 +1,30 @@
+
+
+
diff --git a/lib/default-theme/styles/theme.styl b/lib/default-theme/styles/theme.styl
index a8c513cd5f..a733861fa6 100644
--- a/lib/default-theme/styles/theme.styl
+++ b/lib/default-theme/styles/theme.styl
@@ -4,6 +4,7 @@
@require './custom-blocks'
@require './arrow'
@require './wrapper'
+@require './toc'
html, body
padding 0
diff --git a/lib/default-theme/styles/toc.styl b/lib/default-theme/styles/toc.styl
new file mode 100644
index 0000000000..d3e71069ba
--- /dev/null
+++ b/lib/default-theme/styles/toc.styl
@@ -0,0 +1,3 @@
+.table-of-contents
+ .badge
+ vertical-align middle
diff --git a/lib/markdown/index.js b/lib/markdown/index.js
index 370cee617f..06779ac5ef 100644
--- a/lib/markdown/index.js
+++ b/lib/markdown/index.js
@@ -11,11 +11,12 @@ const emoji = require('markdown-it-emoji')
const anchor = require('markdown-it-anchor')
const toc = require('markdown-it-table-of-contents')
const _slugify = require('./slugify')
-const parseHeaders = require('../util/parseHeaders')
+const { parseHeaders, removeTailHtml } = require('../util/parseHeaders')
+const { compose } = require('../util/shared')
module.exports = ({ markdown = {}} = {}) => {
// allow user config slugify
- const slugify = markdown.slugify || _slugify
+ const slugify = markdown.slugify || compose(removeTailHtml, _slugify)
const md = require('markdown-it')({
html: true,
diff --git a/lib/util/index.js b/lib/util/index.js
index 12caf0e7ac..8918ee3e6b 100644
--- a/lib/util/index.js
+++ b/lib/util/index.js
@@ -1,4 +1,4 @@
-const parseHeaders = require('./parseHeaders')
+const { deeplyParseHeaders } = require('./parseHeaders')
exports.normalizeHeadTag = function (tag) {
if (typeof tag === 'string') {
@@ -32,11 +32,11 @@ exports.inferTitle = function (frontmatter) {
return 'Home'
}
if (frontmatter.data.title) {
- return parseHeaders(frontmatter.data.title)
+ return deeplyParseHeaders(frontmatter.data.title)
}
const match = frontmatter.content.trim().match(/^#+\s+(.*)/)
if (match) {
- return parseHeaders(match[1])
+ return deeplyParseHeaders(match[1])
}
}
@@ -68,11 +68,11 @@ exports.extractHeaders = function (content, include = [], md) {
const res = []
tokens.forEach((t, i) => {
if (t.type === 'heading_open' && include.includes(t.tag)) {
- const title = parseHeaders(tokens[i + 1].content)
+ const title = tokens[i + 1].content
const slug = t.attrs.find(([name]) => name === 'id')[1]
res.push({
level: parseInt(t.tag.slice(1), 10),
- title,
+ title: deeplyParseHeaders(title),
slug: slug || md.slugify(title)
})
}
diff --git a/lib/util/parseHeaders.js b/lib/util/parseHeaders.js
index 2a2c4fee4c..3837eeba39 100644
--- a/lib/util/parseHeaders.js
+++ b/lib/util/parseHeaders.js
@@ -1,3 +1,5 @@
+const { compose } = require('./shared')
+
const parseEmojis = str => {
const emojiData = require('markdown-it-emoji/lib/data/full.json')
return String(str).replace(/:(.+?):/g, (placeholder, key) => emojiData[key] || placeholder)
@@ -17,13 +19,19 @@ const removeMarkdownToken = str => String(str)
.replace(/\*(.*)\*/, '$1') // *
.replace(/_(.*)_/, '$1') // _
-// put here to avoid circular references
-const compose = (...processors) => {
- if (processors.length === 0) return input => input
- if (processors.length === 1) return processors[0]
- return processors.reduce((prev, next) => {
- return (...args) => next(prev(...args))
- })
+exports.removeTailHtml = (str) => {
+ return String(str).replace(/<.*>\s*$/g, '')
}
-module.exports = compose(unescapeHtml, parseEmojis, removeMarkdownToken)
+// only remove some md tokens.
+exports.parseHeaders = compose(
+ unescapeHtml,
+ parseEmojis,
+ removeMarkdownToken
+)
+
+// also clean html in headers.
+exports.deeplyParseHeaders = compose(
+ exports.parseHeaders,
+ exports.removeTailHtml
+)
diff --git a/lib/util/shared.js b/lib/util/shared.js
new file mode 100644
index 0000000000..023f63bd1b
--- /dev/null
+++ b/lib/util/shared.js
@@ -0,0 +1,7 @@
+exports.compose = (...processors) => {
+ if (processors.length === 0) return input => input
+ if (processors.length === 1) return processors[0]
+ return processors.reduce((prev, next) => {
+ return (...args) => next(prev(...args))
+ })
+}