Skip to content

Commit

Permalink
feat: add markdown processing
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 27, 2020
1 parent 21d3cd8 commit 5c47bbb
Show file tree
Hide file tree
Showing 22 changed files with 914 additions and 91 deletions.
3 changes: 0 additions & 3 deletions lib/markdown.ts

This file was deleted.

85 changes: 0 additions & 85 deletions lib/server.ts

This file was deleted.

7 changes: 7 additions & 0 deletions lib/theme-default/Layout.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
<template>
<div class="theme-container">
<h1>Hello VitePress {{ a }}</h1>
<Content/>
</div>
</template>

<script>
export default {
data: () => ({ a: 111 })
}
</script>

<style>
.theme-container {
font-family: Arial, Helvetica, sans-serif;
Expand Down
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,21 @@
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"diacritics": "^1.3.0",
"escape-html": "^1.0.3",
"gray-matter": "^4.0.2",
"lru-cache": "^5.1.1",
"markdown-it": "^10.0.0",
"markdown-it-anchor": "^5.2.7",
"markdown-it-container": "^2.0.0",
"markdown-it-emoji": "^1.4.0",
"markdown-it-table-of-contents": "^0.4.4",
"prismjs": "^1.20.0",
"vite": "^0.6.0"
},
"devDependencies": {
"@types/lru-cache": "^5.1.0",
"@types/markdown-it": "^10.0.1",
"@types/node": "^13.13.4",
"typescript": "^3.8.3"
}
Expand Down
File renamed without changes.
105 changes: 105 additions & 0 deletions src/markdown/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import MarkdownIt from 'markdown-it'
import { RuleBlock } from 'markdown-it/lib/parser_block'

// Replacing the default htmlBlock rule to allow using custom components at
// root level

const blockNames: string[] = require('markdown-it/lib/common/html_blocks')
const HTML_OPEN_CLOSE_TAG_RE: RegExp = 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: [RegExp, RegExp, boolean][] = [
[/^<(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]
]

export const componentPlugin = (md: MarkdownIt) => {
md.block.ruler.at('html_block', htmlBlock)
}

const htmlBlock: RuleBlock = (
state,
startLine,
endLine,
silent
): boolean => {
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) {
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
}
43 changes: 43 additions & 0 deletions src/markdown/containers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import MarkdownIt from 'markdown-it'
import Token from 'markdown-it/lib/token'

const container = require('markdown-it-container')

export const containerPlugin = (md: MarkdownIt) => {
md.use(...createContainer('tip', 'TIP'))
.use(...createContainer('warning', 'WARNING'))
.use(...createContainer('danger', 'WARNING'))
// explicitly escape Vue syntax
.use(container, 'v-pre', {
render: (tokens: Token[], idx: number) =>
tokens[idx].nesting === 1 ? `<div v-pre>\n` : `</div>\n`
})
}

type ContainerArgs = [
typeof container,
string,
{
render(tokens: Token[], idx: number): string
}
]

function createContainer(klass: string, defaultTitle: string): ContainerArgs {
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`
}
}
}
]
}
50 changes: 50 additions & 0 deletions src/markdown/highlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const chalk = require('chalk')
const prism = require('prismjs')
const loadLanguages = require('prismjs/components/index')
const escapeHtml = require('escape-html')

// required to make embedded highlighting work...
loadLanguages(['markup', 'css', 'javascript'])

function wrap(code: string, lang: string): string {
if (lang === 'text') {
code = escapeHtml(code)
}
return `<pre v-pre class="language-${lang}"><code>${code}</code></pre>`
}

export const highlight = (str: string, lang: string) => {
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) {
console.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')
}
50 changes: 50 additions & 0 deletions src/markdown/highlightLines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Modified from https://github.com/egoist/markdown-it-highlight-lines
import MarkdownIt from 'markdown-it'

const RE = /{([\d,-]+)}/
const wrapperRE = /^<pre .*?><code>/

export const highlightLinePlugin = (md: MarkdownIt) => {
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
}
}
16 changes: 16 additions & 0 deletions src/markdown/hoist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import MarkdownIt from 'markdown-it'

export const hoistPlugin = (md: MarkdownIt & { __data: any }) => {
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
}
}
}
Loading

0 comments on commit 5c47bbb

Please sign in to comment.