From 26105034da4fcce7ac883c899d781f016559310d Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 8 Nov 2018 00:38:48 +0800 Subject: switch to vuepress --- node_modules/vuepress/lib/markdown/component.js | 81 +++++++++++++++++++ node_modules/vuepress/lib/markdown/containers.js | 28 +++++++ node_modules/vuepress/lib/markdown/highlight.js | 47 +++++++++++ .../vuepress/lib/markdown/highlightLines.js | 49 ++++++++++++ node_modules/vuepress/lib/markdown/hoist.js | 14 ++++ node_modules/vuepress/lib/markdown/index.js | 78 +++++++++++++++++++ node_modules/vuepress/lib/markdown/lineNumbers.js | 26 +++++++ node_modules/vuepress/lib/markdown/link.js | 91 ++++++++++++++++++++++ node_modules/vuepress/lib/markdown/preWrapper.js | 19 +++++ node_modules/vuepress/lib/markdown/slugify.js | 22 ++++++ node_modules/vuepress/lib/markdown/snippet.js | 43 ++++++++++ 11 files changed, 498 insertions(+) create mode 100644 node_modules/vuepress/lib/markdown/component.js create mode 100644 node_modules/vuepress/lib/markdown/containers.js create mode 100644 node_modules/vuepress/lib/markdown/highlight.js create mode 100644 node_modules/vuepress/lib/markdown/highlightLines.js create mode 100644 node_modules/vuepress/lib/markdown/hoist.js create mode 100644 node_modules/vuepress/lib/markdown/index.js create mode 100644 node_modules/vuepress/lib/markdown/lineNumbers.js create mode 100644 node_modules/vuepress/lib/markdown/link.js create mode 100644 node_modules/vuepress/lib/markdown/preWrapper.js create mode 100644 node_modules/vuepress/lib/markdown/slugify.js create mode 100644 node_modules/vuepress/lib/markdown/snippet.js (limited to 'node_modules/vuepress/lib/markdown') diff --git a/node_modules/vuepress/lib/markdown/component.js b/node_modules/vuepress/lib/markdown/component.js new file mode 100644 index 00000000..1aab66e8 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/component.js @@ -0,0 +1,81 @@ +// Replacing the default htmlBlock rule to allow using custom components at +// root level + +const blockNames = require('markdown-it/lib/common/html_blocks') +const HTML_OPEN_CLOSE_TAG_RE = require('markdown-it/lib/common/html_re').HTML_OPEN_CLOSE_TAG_RE + +// An array of opening and corresponding closing sequences for html tags, +// last argument defines whether it can terminate a paragraph or not +const HTML_SEQUENCES = [ + [/^<(script|pre|style)(?=(\s|>|$))/i, /<\/(script|pre|style)>/i, true], + [/^/, true], + [/^<\?/, /\?>/, true], + [/^/, true], + [/^/, true], + // PascalCase Components + [/^<[A-Z]/, />/, true], + // custom elements with hyphens + [/^<\w+\-/, />/, true], + [new RegExp('^|$))', 'i'), /^$/, true], + [new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + '\\s*$'), /^$/, false] +] + +module.exports = md => { + md.block.ruler.at('html_block', htmlBlock) +} + +function htmlBlock (state, startLine, endLine, silent) { + let i, nextLine, lineText + let pos = state.bMarks[startLine] + state.tShift[startLine] + let max = state.eMarks[startLine] + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { return false } + + if (!state.md.options.html) { return false } + + if (state.src.charCodeAt(pos) !== 0x3C/* < */) { return false } + + lineText = state.src.slice(pos, max) + + for (i = 0; i < HTML_SEQUENCES.length; i++) { + if (HTML_SEQUENCES[i][0].test(lineText)) { break } + } + + if (i === HTML_SEQUENCES.length) { + console.log(lineText) + return false + } + + if (silent) { + // true if this sequence can be a terminator, false otherwise + return HTML_SEQUENCES[i][2] + } + + nextLine = startLine + 1 + + // If we are here - we detected HTML block. + // Let's roll down till block end. + if (!HTML_SEQUENCES[i][1].test(lineText)) { + for (; nextLine < endLine; nextLine++) { + if (state.sCount[nextLine] < state.blkIndent) { break } + + pos = state.bMarks[nextLine] + state.tShift[nextLine] + max = state.eMarks[nextLine] + lineText = state.src.slice(pos, max) + + if (HTML_SEQUENCES[i][1].test(lineText)) { + if (lineText.length !== 0) { nextLine++ } + break + } + } + } + + state.line = nextLine + + const token = state.push('html_block', '', 0) + token.map = [startLine, nextLine] + token.content = state.getLines(startLine, nextLine, state.blkIndent, true) + + return true +} diff --git a/node_modules/vuepress/lib/markdown/containers.js b/node_modules/vuepress/lib/markdown/containers.js new file mode 100644 index 00000000..215c4534 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/containers.js @@ -0,0 +1,28 @@ +const container = require('markdown-it-container') + +module.exports = md => { + md + .use(...createContainer('tip', 'TIP')) + .use(...createContainer('warning', 'WARNING')) + .use(...createContainer('danger', 'WARNING')) + // explicitly escape Vue syntax + .use(container, 'v-pre', { + render: (tokens, idx) => tokens[idx].nesting === 1 + ? `
\n` + : `
\n` + }) +} + +function createContainer (klass, defaultTitle) { + return [container, klass, { + render (tokens, idx) { + const token = tokens[idx] + const info = token.info.trim().slice(klass.length).trim() + if (token.nesting === 1) { + return `

${info || defaultTitle}

\n` + } else { + return `
\n` + } + } + }] +} diff --git a/node_modules/vuepress/lib/markdown/highlight.js b/node_modules/vuepress/lib/markdown/highlight.js new file mode 100644 index 00000000..02d5e3ff --- /dev/null +++ b/node_modules/vuepress/lib/markdown/highlight.js @@ -0,0 +1,47 @@ +const chalk = require('chalk') +const prism = require('prismjs') +const loadLanguages = require('prismjs/components/index') +const escapeHtml = require('escape-html') +const logger = require('../util/logger') + +// required to make embedded highlighting work... +loadLanguages(['markup', 'css', 'javascript']) + +function wrap (code, lang) { + if (lang === 'text') { + code = escapeHtml(code) + } + return `
${code}
` +} + +module.exports = (str, lang) => { + if (!lang) { + return wrap(str, 'text') + } + lang = lang.toLowerCase() + const rawLang = lang + if (lang === 'vue' || lang === 'html') { + lang = 'markup' + } + if (lang === 'md') { + lang = 'markdown' + } + if (lang === 'ts') { + lang = 'typescript' + } + if (lang === 'py') { + lang = 'python' + } + if (!prism.languages[lang]) { + try { + loadLanguages([lang]) + } catch (e) { + logger.warn(chalk.yellow(`[vuepress] Syntax highlight for language "${lang}" is not supported.`)) + } + } + if (prism.languages[lang]) { + const code = prism.highlight(str, prism.languages[lang], lang) + return wrap(code, rawLang) + } + return wrap(str, 'text') +} diff --git a/node_modules/vuepress/lib/markdown/highlightLines.js b/node_modules/vuepress/lib/markdown/highlightLines.js new file mode 100644 index 00000000..dd833432 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/highlightLines.js @@ -0,0 +1,49 @@ +// Modified from https://github.com/egoist/markdown-it-highlight-lines + +const RE = /{([\d,-]+)}/ +const wrapperRE = /^
/
+
+module.exports = md => {
+  const fence = md.renderer.rules.fence
+  md.renderer.rules.fence = (...args) => {
+    const [tokens, idx, options] = args
+    const token = tokens[idx]
+
+    const rawInfo = token.info
+    if (!rawInfo || !RE.test(rawInfo)) {
+      return fence(...args)
+    }
+
+    const langName = rawInfo.replace(RE, '').trim()
+    // ensure the next plugin get the correct lang.
+    token.info = langName
+
+    const lineNumbers = RE.exec(rawInfo)[1]
+      .split(',')
+      .map(v => v.split('-').map(v => parseInt(v, 10)))
+
+    const code = options.highlight
+      ? options.highlight(token.content, langName)
+      : token.content
+
+    const rawCode = code.replace(wrapperRE, '')
+    const highlightLinesCode = rawCode.split('\n').map((split, index) => {
+      const lineNumber = index + 1
+      const inRange = lineNumbers.some(([start, end]) => {
+        if (start && end) {
+          return lineNumber >= start && lineNumber <= end
+        }
+        return lineNumber === start
+      })
+      if (inRange) {
+        return `
 
` + } + return '
' + }).join('') + + const highlightLinesWrapperCode = + `
${highlightLinesCode}
` + + return highlightLinesWrapperCode + code + } +} diff --git a/node_modules/vuepress/lib/markdown/hoist.js b/node_modules/vuepress/lib/markdown/hoist.js new file mode 100644 index 00000000..a6383398 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/hoist.js @@ -0,0 +1,14 @@ +module.exports = md => { + const RE = /^<(script|style)(?=(\s|>|$))/i + + md.renderer.rules.html_block = (tokens, idx) => { + const content = tokens[idx].content + const hoistedTags = md.__data.hoistedTags || (md.__data.hoistedTags = []) + if (RE.test(content.trim())) { + hoistedTags.push(content) + return '' + } else { + return content + } + } +} diff --git a/node_modules/vuepress/lib/markdown/index.js b/node_modules/vuepress/lib/markdown/index.js new file mode 100644 index 00000000..f4942f6c --- /dev/null +++ b/node_modules/vuepress/lib/markdown/index.js @@ -0,0 +1,78 @@ +const highlight = require('./highlight') +const highlightLines = require('./highlightLines') +const preWrapper = require('./preWrapper') +const lineNumbers = require('./lineNumbers') +const component = require('./component') +const hoistScriptStyle = require('./hoist') +const convertRouterLink = require('./link') +const containers = require('./containers') +const snippet = require('./snippet') +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') + +module.exports = ({ markdown = {}} = {}) => { + // allow user config slugify + const slugify = markdown.slugify || _slugify + + const md = require('markdown-it')({ + html: true, + highlight + }) + // custom plugins + .use(component) + .use(highlightLines) + .use(preWrapper) + .use(snippet) + .use(convertRouterLink, Object.assign({ + target: '_blank', + rel: 'noopener noreferrer' + }, markdown.externalLinks)) + .use(hoistScriptStyle) + .use(containers) + + // 3rd party plugins + .use(emoji) + .use(anchor, Object.assign({ + slugify, + permalink: true, + permalinkBefore: true, + permalinkSymbol: '#' + }, markdown.anchor)) + .use(toc, Object.assign({ + slugify, + includeLevel: [2, 3], + format: parseHeaders + }, markdown.toc)) + + // apply user config + if (markdown.config) { + markdown.config(md) + } + + if (markdown.lineNumbers) { + md.use(lineNumbers) + } + + module.exports.dataReturnable(md) + + // expose slugify + md.slugify = slugify + + return md +} + +module.exports.dataReturnable = function dataReturnable (md) { + // override render to allow custom plugins return data + const render = md.render + md.render = (...args) => { + md.__data = {} + const html = render.call(md, ...args) + return { + html, + data: md.__data + } + } +} diff --git a/node_modules/vuepress/lib/markdown/lineNumbers.js b/node_modules/vuepress/lib/markdown/lineNumbers.js new file mode 100644 index 00000000..bc95d2b0 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/lineNumbers.js @@ -0,0 +1,26 @@ +// markdown-it plugin for generating line numbers. +// It depends on preWrapper plugin. + +module.exports = md => { + const fence = md.renderer.rules.fence + md.renderer.rules.fence = (...args) => { + const rawCode = fence(...args) + const code = rawCode.slice( + rawCode.indexOf(''), + rawCode.indexOf('') + ) + + const lines = code.split('\n') + const lineNumbersCode = [...Array(lines.length - 1)] + .map((line, index) => `${index + 1}
`).join('') + + const lineNumbersWrapperCode = + `
${lineNumbersCode}
` + + const finalCode = rawCode + .replace('', `${lineNumbersWrapperCode}`) + .replace('extra-class', 'line-numbers-mode') + + return finalCode + } +} diff --git a/node_modules/vuepress/lib/markdown/link.js b/node_modules/vuepress/lib/markdown/link.js new file mode 100644 index 00000000..bbe9550d --- /dev/null +++ b/node_modules/vuepress/lib/markdown/link.js @@ -0,0 +1,91 @@ +// markdown-it plugin for: +// 1. adding target="_blank" to external links +// 2. converting internal links to + +const indexRE = /(.*)(index|readme).md(#?.*)$/i + +module.exports = (md, externalAttrs) => { + let hasOpenRouterLink = false + let hasOpenExternalLink = false + + md.renderer.rules.link_open = (tokens, idx, options, env, self) => { + const token = tokens[idx] + const hrefIndex = token.attrIndex('href') + if (hrefIndex >= 0) { + const link = token.attrs[hrefIndex] + const href = link[1] + const isExternal = /^https?:/.test(href) + const isSourceLink = /(\/|\.md|\.html)(#.*)?$/.test(href) + if (isExternal) { + Object.entries(externalAttrs).forEach(([key, val]) => { + token.attrSet(key, val) + }) + if (/_blank/i.test(externalAttrs['target'])) { + hasOpenExternalLink = true + } + } else if (isSourceLink) { + hasOpenRouterLink = true + tokens[idx] = toRouterLink(token, link) + } + } + return self.renderToken(tokens, idx, options) + } + + function toRouterLink (token, link) { + link[0] = 'to' + let to = link[1] + + // convert link to filename and export it for existence check + const links = md.__data.links || (md.__data.links = []) + links.push(to) + + const indexMatch = to.match(indexRE) + if (indexMatch) { + const [, path, , hash] = indexMatch + to = path + hash + } else { + to = to + .replace(/\.md$/, '.html') + .replace(/\.md(#.*)$/, '.html$1') + } + + // relative path usage. + if (!to.startsWith('/')) { + to = ensureBeginningDotSlash(to) + } + + // markdown-it encodes the uri + link[1] = decodeURI(to) + + // export the router links for testing + const routerLinks = md.__data.routerLinks || (md.__data.routerLinks = []) + routerLinks.push(to) + + return Object.assign({}, token, { + tag: 'router-link' + }) + } + + md.renderer.rules.link_close = (tokens, idx, options, env, self) => { + const token = tokens[idx] + if (hasOpenRouterLink) { + token.tag = 'router-link' + hasOpenRouterLink = false + } + if (hasOpenExternalLink) { + hasOpenExternalLink = false + // add OutBoundLink to the beforeend of this link if it opens in _blank. + return '' + self.renderToken(tokens, idx, options) + } + return self.renderToken(tokens, idx, options) + } +} + +const beginningSlashRE = /^\.\// + +function ensureBeginningDotSlash (path) { + if (beginningSlashRE.test(path)) { + return path + } + return './' + path +} diff --git a/node_modules/vuepress/lib/markdown/preWrapper.js b/node_modules/vuepress/lib/markdown/preWrapper.js new file mode 100644 index 00000000..8f1b1d73 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/preWrapper.js @@ -0,0 +1,19 @@ +// markdown-it plugin for wrapping
 ... 
. +// +// If your plugin was chained before preWrapper, you can add additional eleemnt directly. +// If your plugin was chained after preWrapper, you can use these slots: +// 1. +// 2. +// 3. +// 4. + +module.exports = md => { + const fence = md.renderer.rules.fence + md.renderer.rules.fence = (...args) => { + const [tokens, idx] = args + const token = tokens[idx] + const rawCode = fence(...args) + return `
` + + `${rawCode}
` + } +} diff --git a/node_modules/vuepress/lib/markdown/slugify.js b/node_modules/vuepress/lib/markdown/slugify.js new file mode 100644 index 00000000..47a9d6e9 --- /dev/null +++ b/node_modules/vuepress/lib/markdown/slugify.js @@ -0,0 +1,22 @@ +// string.js slugify drops non ascii chars so we have to +// use a custom implementation here +const removeDiacritics = require('diacritics').remove +// eslint-disable-next-line no-control-regex +const rControl = /[\u0000-\u001f]/g +const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'<>,.?/]+/g + +module.exports = function slugify (str) { + return removeDiacritics(str) + // Remove control characters + .replace(rControl, '') + // Replace special characters + .replace(rSpecial, '-') + // Remove continous separators + .replace(/\-{2,}/g, '-') + // Remove prefixing and trailing separtors + .replace(/^\-+|\-+$/g, '') + // ensure it doesn't start with a number (#121) + .replace(/^(\d)/, '_$1') + // lowercase + .toLowerCase() +} diff --git a/node_modules/vuepress/lib/markdown/snippet.js b/node_modules/vuepress/lib/markdown/snippet.js new file mode 100644 index 00000000..a677e12f --- /dev/null +++ b/node_modules/vuepress/lib/markdown/snippet.js @@ -0,0 +1,43 @@ +const fs = require('fs') + +module.exports = function snippet (md, options = {}) { + const root = options.root || process.cwd() + function parser (state, startLine, endLine, silent) { + const CH = '<'.charCodeAt(0) + const pos = state.bMarks[startLine] + state.tShift[startLine] + const max = state.eMarks[startLine] + + // if it's indented more than 3 spaces, it should be a code block + if (state.sCount[startLine] - state.blkIndent >= 4) { + return false + } + + for (let i = 0; i < 3; ++i) { + const ch = state.src.charCodeAt(pos + i) + if (ch !== CH || pos + i >= max) return false + } + + if (silent) { + return true + } + + const start = pos + 3 + const end = state.skipSpacesBack(max, pos) + const rawPath = state.src.slice(start, end).trim().replace(/^@/, root) + const filename = rawPath.split(/[{:\s]/).shift() + const content = fs.existsSync(filename) ? fs.readFileSync(filename).toString() : 'Not found: ' + filename + const meta = rawPath.replace(filename, '') + + state.line = startLine + 1 + + const token = state.push('fence', 'code', 0) + token.info = filename.split('.').pop() + meta + token.content = content + token.markup = '```' + token.map = [startLine, startLine + 1] + + return true + } + + md.block.ruler.before('fence', 'snippet', parser) +} -- cgit v1.2.3