aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vuepress/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vuepress/lib/util')
-rw-r--r--node_modules/vuepress/lib/util/index.js83
-rw-r--r--node_modules/vuepress/lib/util/logger.js47
-rw-r--r--node_modules/vuepress/lib/util/parseHeaders.js56
-rw-r--r--node_modules/vuepress/lib/util/shared.js7
4 files changed, 193 insertions, 0 deletions
diff --git a/node_modules/vuepress/lib/util/index.js b/node_modules/vuepress/lib/util/index.js
new file mode 100644
index 00000000..52044a8f
--- /dev/null
+++ b/node_modules/vuepress/lib/util/index.js
@@ -0,0 +1,83 @@
+const { deeplyParseHeaders } = require('./parseHeaders')
+
+exports.normalizeHeadTag = function (tag) {
+ if (typeof tag === 'string') {
+ tag = [tag]
+ }
+ const tagName = tag[0]
+ return {
+ tagName,
+ attributes: tag[1] || {},
+ innerHTML: tag[2] || '',
+ closeTag: !(tagName === 'meta' || tagName === 'link')
+ }
+}
+
+exports.applyUserWebpackConfig = function (userConfig, config, isServer) {
+ const merge = require('webpack-merge')
+ if (typeof userConfig === 'object') {
+ return merge(config, userConfig)
+ }
+ if (typeof userConfig === 'function') {
+ const res = userConfig(config, isServer)
+ if (res && typeof res === 'object') {
+ return merge(config, res)
+ }
+ }
+ return config
+}
+
+exports.inferTitle = function (frontmatter) {
+ if (frontmatter.data.home) {
+ return 'Home'
+ }
+ if (frontmatter.data.title) {
+ return deeplyParseHeaders(frontmatter.data.title)
+ }
+ const match = frontmatter.content.trim().match(/^#+\s+(.*)/m)
+ if (match) {
+ return deeplyParseHeaders(match[1])
+ }
+}
+
+exports.parseFrontmatter = function (content) {
+ const matter = require('gray-matter')
+ const toml = require('toml')
+
+ return matter(content, {
+ excerpt_separator: '<!-- more -->',
+ engines: {
+ toml: toml.parse.bind(toml),
+ excerpt: false
+ }
+ })
+}
+
+const LRU = require('lru-cache')
+const cache = LRU({ max: 1000 })
+
+exports.extractHeaders = function (content, include = [], md) {
+ const key = content + include.join(',')
+ const hit = cache.get(key)
+ if (hit) {
+ return hit
+ }
+
+ const tokens = md.parse(content, {})
+
+ const res = []
+ tokens.forEach((t, i) => {
+ if (t.type === 'heading_open' && include.includes(t.tag)) {
+ 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: deeplyParseHeaders(title),
+ slug: slug || md.slugify(title)
+ })
+ }
+ })
+
+ cache.set(key, res)
+ return res
+}
diff --git a/node_modules/vuepress/lib/util/logger.js b/node_modules/vuepress/lib/util/logger.js
new file mode 100644
index 00000000..84e07e86
--- /dev/null
+++ b/node_modules/vuepress/lib/util/logger.js
@@ -0,0 +1,47 @@
+const chalk = require('chalk')
+
+const logger = {}
+
+const logTypes = {
+ success: {
+ color: 'green',
+ label: 'DONE'
+ },
+ error: {
+ color: 'red',
+ label: 'FAIL'
+ },
+ warn: {
+ color: 'yellow',
+ label: 'WARN'
+ },
+ tip: {
+ color: 'cyan',
+ label: 'TIP'
+ },
+ wait: {
+ color: 'blue',
+ label: 'WAIT'
+ }
+}
+
+const getLoggerFn = (color, label) => (msg, log = true) => {
+ let newLine = false
+ if (msg.startsWith('\n')) {
+ if (log) msg = msg.slice(1)
+ newLine = true
+ }
+ msg = chalk.reset.inverse.bold[color](` ${label} `) + ' ' + msg
+ if (log) {
+ console.log(newLine ? '\n' + msg : msg)
+ } else {
+ return msg
+ }
+}
+
+for (const type in logTypes) {
+ const { color, label } = logTypes[type]
+ logger[type] = getLoggerFn(color, label)
+}
+
+module.exports = logger
diff --git a/node_modules/vuepress/lib/util/parseHeaders.js b/node_modules/vuepress/lib/util/parseHeaders.js
new file mode 100644
index 00000000..2c9fe641
--- /dev/null
+++ b/node_modules/vuepress/lib/util/parseHeaders.js
@@ -0,0 +1,56 @@
+// 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 markdiwn
+// 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.
+
+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)
+}
+
+const unescapeHtml = html => String(html)
+ .replace(/&quot;/g, '"')
+ .replace(/&#39;/g, '\'')
+ .replace(/&#x3A;/g, ':')
+ .replace(/&lt;/g, '<')
+ .replace(/&gt;/g, '>')
+
+const removeMarkdownTokens = str => String(str)
+ .replace(/\[(.*)\]\(.*\)/, '$1') // []()
+ .replace(/(`|\*{1,3}|_)(.*?[^\\])\1/g, '$2') // `{t}` | *{t}* | **{t}** | ***{t}*** | _{t}_
+ .replace(/(\\)(\*|_|`|\!)/g, '$2') // remove escape char '\'
+
+const trim = str => 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"
+exports.removeNonCodeWrappedHTML = (str) => {
+ return String(str).replace(/(^|[^><`])<.*>([^><`]|$)/g, '$1$2')
+}
+
+// Unescape html, parse emojis and remove some md tokens.
+exports.parseHeaders = compose(
+ unescapeHtml,
+ parseEmojis,
+ 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
+exports.deeplyParseHeaders = compose(
+ exports.removeNonCodeWrappedHTML,
+ exports.parseHeaders,
+)
diff --git a/node_modules/vuepress/lib/util/shared.js b/node_modules/vuepress/lib/util/shared.js
new file mode 100644
index 00000000..023f63bd
--- /dev/null
+++ b/node_modules/vuepress/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))
+ })
+}