❗ 重命名 docs 为 example
This commit is contained in:
36
example/src/plugin/common-plugins.ts
Normal file
36
example/src/plugin/common-plugins.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import Markdown from 'vite-plugin-md'
|
||||
import container from 'markdown-it-container'
|
||||
import highlight from './highlight'
|
||||
import snippet from './snippet'
|
||||
import demo from './demo'
|
||||
import createTitle from './create-title'
|
||||
import createBlock from './create-block'
|
||||
import createDescribe from './create-describe'
|
||||
import createTable from './create-table'
|
||||
import preWrapper from './pre-wrapper'
|
||||
|
||||
const plugins = [
|
||||
vue({
|
||||
include: [/\.vue$/, /\.md$/],
|
||||
}),
|
||||
Markdown({
|
||||
markdownItOptions: {
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
highlight,
|
||||
},
|
||||
markdownItSetup(md) {
|
||||
md.use(snippet)
|
||||
.use(preWrapper)
|
||||
.use(container, 'demo', demo)
|
||||
.use(...createTable('table', ''))
|
||||
.use(...createBlock('block', ''))
|
||||
.use(...createTitle('title', ''))
|
||||
.use(...createDescribe('describe', ''))
|
||||
},
|
||||
}),
|
||||
] as any
|
||||
|
||||
export default plugins
|
||||
31
example/src/plugin/create-block.ts
Normal file
31
example/src/plugin/create-block.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import container from 'markdown-it-container'
|
||||
import type Token from 'markdown-it/lib/token'
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{
|
||||
render(tokens: Token[], idx: number): string
|
||||
}
|
||||
]
|
||||
|
||||
export default 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 `<lay-block style="margin-left:8px;margin-bottom:40px;">${info}`
|
||||
} else {
|
||||
return '</lay-block>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
31
example/src/plugin/create-describe.ts
Normal file
31
example/src/plugin/create-describe.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import container from 'markdown-it-container'
|
||||
import type Token from 'markdown-it/lib/token'
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{
|
||||
render(tokens: Token[], idx: number): string
|
||||
}
|
||||
]
|
||||
|
||||
export default 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 `<p style="margin-left: 24px;margin-bottom:20px;">${info}`
|
||||
} else {
|
||||
return '</p>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
31
example/src/plugin/create-table.ts
Normal file
31
example/src/plugin/create-table.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import container from 'markdown-it-container'
|
||||
import type Token from 'markdown-it/lib/token'
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{
|
||||
render(tokens: Token[], idx: number): string
|
||||
}
|
||||
]
|
||||
|
||||
export default 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 `<lay-table-box>${info}`
|
||||
} else {
|
||||
return '</lay-table-box>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
33
example/src/plugin/create-title.ts
Normal file
33
example/src/plugin/create-title.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import container from 'markdown-it-container'
|
||||
import type Token from 'markdown-it/lib/token'
|
||||
|
||||
type ContainerArgs = [
|
||||
typeof container,
|
||||
string,
|
||||
{
|
||||
render(tokens: Token[], idx: number): string
|
||||
}
|
||||
]
|
||||
|
||||
export default 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 `<lay-field title="${
|
||||
info || defaultTitle
|
||||
}" style="margin-top:20px;margin-bottom: 20px;">`
|
||||
} else {
|
||||
return '</lay-field>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
141
example/src/plugin/demo.ts
Normal file
141
example/src/plugin/demo.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import markdown from 'markdown-it'
|
||||
import highlight from './highlight'
|
||||
import type Token from 'markdown-it/lib/token'
|
||||
|
||||
function assignScript(script: string) {
|
||||
const dependencies = {} as Record<string, string[]>
|
||||
const attrs = {} as Record<string, string>
|
||||
const content = script
|
||||
.replace(/import\s?\{.*\}.*/g, (item) => {
|
||||
const key = getInnerString(item.replace(/'/g, '"'), '"', '"')
|
||||
const value = getInnerString(item.replace(/\s+/g, ''), '{', '}')
|
||||
const list = value ? value.split(',') : []
|
||||
if (key && dependencies[key]) {
|
||||
dependencies[key] = dependencies[key].concat(list)
|
||||
} else if (key) {
|
||||
dependencies[key] = list
|
||||
}
|
||||
return ''
|
||||
})
|
||||
/**
|
||||
* const -> let
|
||||
*
|
||||
* const a = -> let a =
|
||||
* const a = -> a =
|
||||
*/
|
||||
.replace(/(const|let|var)\s\w*\s?=/g, (item) => {
|
||||
const attr = getInnerString(item, '\\s', '\\s?=')
|
||||
if (attr && !(attr in attrs)) {
|
||||
attrs[attr] = attr
|
||||
return `let ${attr} =`
|
||||
} else {
|
||||
return attr + ' ='
|
||||
}
|
||||
})
|
||||
// Remove extra line breaks
|
||||
.replace(/\n+/gm, '\n')
|
||||
// Combine the import
|
||||
const reImport = Object.keys(dependencies).reduce((all, item) => {
|
||||
const filterAttrs = [...new Set(dependencies[item])]
|
||||
return all + `import {${filterAttrs + ','}} from '${item}';\n`
|
||||
}, '')
|
||||
|
||||
return reImport + content
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract part of the new string from the middle of the string
|
||||
* @param {string} string string
|
||||
* @param {string} prefix RegExp string
|
||||
* @param {string} postfix RegExp string
|
||||
* @param {string} type g | m | i
|
||||
*/
|
||||
function getInnerString(
|
||||
string: string,
|
||||
prefix: string,
|
||||
postfix = '',
|
||||
type: 'i' | 'g' | 'm' = 'i'
|
||||
): string | undefined {
|
||||
const result = new RegExp(`${prefix}(.*)${postfix}`, type)
|
||||
const match = string.match(result)
|
||||
return match ? match[1].trim() : undefined
|
||||
}
|
||||
|
||||
let script = '' // Record the <script> label of the current page
|
||||
|
||||
export default {
|
||||
render: (tokens: Token[], idx: number): string => {
|
||||
// the `demo` block of the current page
|
||||
const htmlBlock = tokens.filter((item) => item.type === 'html_block')
|
||||
const { nesting, info = '', map } = tokens[idx]
|
||||
|
||||
if (nesting === -1) {
|
||||
return '</lay-code>'
|
||||
}
|
||||
|
||||
const matchedInfo = info.trim().match(/^demo\s+(.*)$/)
|
||||
const description = matchedInfo && matchedInfo[1]
|
||||
const descTemplate = markdown().render(description || '')
|
||||
let str = '' // copy the current `demo` block code
|
||||
let lastLine = NaN
|
||||
|
||||
for (let i = 0; i < htmlBlock.length; i++) {
|
||||
const item = htmlBlock[i]
|
||||
|
||||
if (item.map && map && item.map[0] >= map[0] && item.map[1] <= map[1]) {
|
||||
const { map, content } = item
|
||||
const delta = map[0] - (lastLine || map[1])
|
||||
|
||||
if (delta > 0) {
|
||||
str += '\n'.repeat(delta)
|
||||
}
|
||||
str += content
|
||||
lastLine = map[1]
|
||||
if (i === 0) {
|
||||
script = ''
|
||||
}
|
||||
// Remove top <template>
|
||||
if (/^<template>/.test(content)) {
|
||||
const reContent = content.match(/^<template>((\s|\S)*)<\/template>/m)
|
||||
|
||||
htmlBlock[i].content = (reContent && reContent[1]) || ''
|
||||
}
|
||||
// Extract the <script> label content
|
||||
if (content.includes('<script')) {
|
||||
if (/export\sdefault\s?\{/m.test(content)) {
|
||||
const setup = content.match(
|
||||
/setup\s?\(\)\s?\{((\s|\S)*)return\s?\{/m
|
||||
)
|
||||
const reContent = content.replace(
|
||||
/export\sdefault\s?\{((\s|\S)*)\}/m,
|
||||
(setup && setup[1]) || ''
|
||||
)
|
||||
const reScript = reContent.match(
|
||||
/^<script\s?.*?>((\s|\S)*)<\/script>/m
|
||||
)
|
||||
script += (reScript && reScript[1]) || ''
|
||||
} else {
|
||||
const reScript = content.match(
|
||||
/^<script\s?.*?>((\s|\S)*)<\/script>/m
|
||||
)
|
||||
script += (reScript && reScript[1]) || ''
|
||||
}
|
||||
htmlBlock[i].content = ''
|
||||
}
|
||||
// Change the last content to <script> of the current page
|
||||
if (i + 1 === htmlBlock.length) {
|
||||
htmlBlock[i].content = `
|
||||
<script setup>
|
||||
${assignScript(script)}
|
||||
</script>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<lay-code>
|
||||
${description ? `<template #description>${descTemplate}</template>` : ''}
|
||||
<template #code>${highlight(str, 'vue')}</template>
|
||||
`
|
||||
},
|
||||
}
|
||||
44
example/src/plugin/highlight.ts
Normal file
44
example/src/plugin/highlight.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import prism from 'prismjs'
|
||||
import loadLanguages from 'prismjs/components/index'
|
||||
import escapeHtml from 'escape-html'
|
||||
|
||||
loadLanguages(['markup', 'css', 'javascript'])
|
||||
|
||||
function wrap(code: string, lang: string): string {
|
||||
if (lang === 'text') {
|
||||
code = escapeHtml(code)
|
||||
}
|
||||
return `<pre v-pre><code>${code}</code></pre>`
|
||||
}
|
||||
|
||||
export default (str: string, lang: string): 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(lang, e)
|
||||
}
|
||||
}
|
||||
if (prism.languages[lang]) {
|
||||
const code = prism.highlight(str, prism.languages[lang], lang)
|
||||
return wrap(code, rawLang)
|
||||
}
|
||||
return wrap(str, 'text')
|
||||
}
|
||||
11
example/src/plugin/pre-wrapper.ts
Normal file
11
example/src/plugin/pre-wrapper.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import MarkdownIt from 'markdown-it'
|
||||
|
||||
export default (md: MarkdownIt): void => {
|
||||
const fence = md.renderer.rules.fence!
|
||||
md.renderer.rules.fence = (...args) => {
|
||||
const [tokens, idx] = args
|
||||
const token = tokens[idx]
|
||||
const rawCode = fence(...args)
|
||||
return `<div class="language-${token.info.trim()}">${rawCode}</div>`
|
||||
}
|
||||
}
|
||||
39
example/src/plugin/snippet.ts
Normal file
39
example/src/plugin/snippet.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import fs from 'fs'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { RuleBlock } from 'markdown-it/lib/parser_block'
|
||||
|
||||
export default (md: MarkdownIt): void => {
|
||||
const parser: RuleBlock = (state, startLine, endLine, silent) => {
|
||||
const CH = '<'.charCodeAt(0)
|
||||
const pos = state.bMarks[startLine] + state.tShift[startLine]
|
||||
const max = state.eMarks[startLine]
|
||||
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(/^@/, process.cwd())
|
||||
const content = fs.existsSync(rawPath)
|
||||
? fs.readFileSync(rawPath).toString()
|
||||
: 'Not found: ' + rawPath
|
||||
const meta = rawPath.replace(rawPath, '')
|
||||
state.line = startLine + 1
|
||||
const token = state.push('fence', 'code', 0)
|
||||
token.info = rawPath.split('.').pop() + meta
|
||||
token.content = content
|
||||
token.markup = '```'
|
||||
token.map = [startLine, startLine + 1]
|
||||
return true
|
||||
}
|
||||
md.block.ruler.before('fence', 'snippet', parser)
|
||||
}
|
||||
Reference in New Issue
Block a user