aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vuepress/lib/markdown
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vuepress/lib/markdown')
-rw-r--r--node_modules/vuepress/lib/markdown/component.js81
-rw-r--r--node_modules/vuepress/lib/markdown/containers.js28
-rw-r--r--node_modules/vuepress/lib/markdown/highlight.js47
-rw-r--r--node_modules/vuepress/lib/markdown/highlightLines.js49
-rw-r--r--node_modules/vuepress/lib/markdown/hoist.js14
-rw-r--r--node_modules/vuepress/lib/markdown/index.js78
-rw-r--r--node_modules/vuepress/lib/markdown/lineNumbers.js26
-rw-r--r--node_modules/vuepress/lib/markdown/link.js91
-rw-r--r--node_modules/vuepress/lib/markdown/preWrapper.js19
-rw-r--r--node_modules/vuepress/lib/markdown/slugify.js22
-rw-r--r--node_modules/vuepress/lib/markdown/snippet.js43
11 files changed, 498 insertions, 0 deletions
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],
+ [/^<![A-Z]/, />/, true],
+ [/^<!\[CDATA\[/, /\]\]>/, true],
+ // PascalCase Components
+ [/^<[A-Z]/, />/, true],
+ // custom elements with hyphens
+ [/^<\w+\-/, />/, true],
+ [new RegExp('^</?(' + blockNames.join('|') + ')(?=(\\s|/?>|$))', '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
+ ? `<div v-pre>\n`
+ : `</div>\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 `<div class="${klass} custom-block"><p class="custom-block-title">${info || defaultTitle}</p>\n`
+ } else {
+ return `</div>\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 `<pre v-pre class="language-${lang}"><code>${code}</code></pre>`
+}
+
+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 = /^<pre .*?><code>/
+
+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 `<div class="highlighted">&nbsp;</div>`
+ }
+ return '<br>'
+ }).join('')
+
+ const highlightLinesWrapperCode =
+ `<div class="highlight-lines">${highlightLinesCode}</div>`
+
+ 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('<code>'),
+ rawCode.indexOf('</code>')
+ )
+
+ const lines = code.split('\n')
+ const lineNumbersCode = [...Array(lines.length - 1)]
+ .map((line, index) => `<span class="line-number">${index + 1}</span><br>`).join('')
+
+ const lineNumbersWrapperCode =
+ `<div class="line-numbers-wrapper">${lineNumbersCode}</div>`
+
+ const finalCode = rawCode
+ .replace('<!--beforeend-->', `${lineNumbersWrapperCode}<!--beforeend-->`)
+ .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 <router-link>
+
+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 '<OutboundLink/>' + 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 <pre> ... </pre>.
+//
+// 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. <!--beforebegin-->
+// 2. <!--afterbegin-->
+// 3. <!--beforeend-->
+// 4. <!--afterend-->
+
+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 `<!--beforebegin--><div class="language-${token.info.trim()} extra-class">` +
+ `<!--afterbegin-->${rawCode}<!--beforeend--></div><!--afterend-->`
+ }
+}
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)
+}