115 lines
3.2 KiB
TypeScript
115 lines
3.2 KiB
TypeScript
import type { RenderRule } from 'markdown-it/lib/renderer.mjs'
|
|
import type Token from 'markdown-it/lib/token.mjs'
|
|
import type { App } from 'vuepress'
|
|
import type { Markdown } from 'vuepress/markdown'
|
|
import type { DemoContainerRender, DemoMeta, MarkdownDemoEnv } from '../../shared/demo.js'
|
|
import container from 'markdown-it-container'
|
|
import { createEmbedRuleBlock } from '../embed/createEmbedRuleBlock.js'
|
|
import { resolveAttrs } from '../utils/resolveAttrs.js'
|
|
import { markdownContainerRender, markdownEmbed } from './markdown.js'
|
|
import { normalContainerRender, normalEmbed } from './normal.js'
|
|
import { normalizeAlias } from './supports/alias.js'
|
|
import { vueContainerRender, vueEmbed } from './vue.js'
|
|
|
|
export function demoEmbed(app: App, md: Markdown): void {
|
|
createEmbedRuleBlock<DemoMeta>(md, {
|
|
type: 'demo',
|
|
syntaxPattern: /^@\[demo(?:\s(vue|normal|markdown))?\s?(.*)\]\((.*)\)/,
|
|
meta: ([, type, info, url]) => ({
|
|
type: (type || 'normal') as DemoMeta['type'],
|
|
url,
|
|
...resolveAttrs(info).attrs,
|
|
}),
|
|
content: (meta, content, env: MarkdownDemoEnv) => {
|
|
const { url, type } = meta
|
|
if (!url) {
|
|
console.warn('[vuepress-plugin-md-power] Invalid demo url: ', url)
|
|
return content
|
|
}
|
|
if (type === 'vue') {
|
|
return vueEmbed(app, md, env, meta)
|
|
}
|
|
|
|
if (type === 'normal') {
|
|
return normalEmbed(app, md, env, meta)
|
|
}
|
|
|
|
if (type === 'markdown') {
|
|
return markdownEmbed(app, md, env, meta)
|
|
}
|
|
|
|
return content
|
|
},
|
|
})
|
|
}
|
|
|
|
const INFO_RE = /(vue|normal|markdown)?\s?(.*)/
|
|
const renderMap: Record<string, DemoContainerRender> = {
|
|
vue: vueContainerRender,
|
|
normal: normalContainerRender,
|
|
markdown: markdownContainerRender,
|
|
}
|
|
|
|
export function demoContainer(app: App, md: Markdown): void {
|
|
let currentRender: DemoContainerRender | undefined
|
|
const render: RenderRule = (
|
|
tokens: Token[],
|
|
index: number,
|
|
_,
|
|
env: MarkdownDemoEnv,
|
|
): string => {
|
|
const token = tokens[index]
|
|
|
|
if (token.nesting === 1) {
|
|
const meta = getContainerMeta(token.info)
|
|
meta.url = `${index}`
|
|
currentRender = renderMap[meta.type]
|
|
return currentRender?.before(
|
|
app,
|
|
md,
|
|
env,
|
|
meta,
|
|
parseCodeMapping(tokens, index, currentRender.token),
|
|
) || ''
|
|
}
|
|
else {
|
|
const res = currentRender?.after() || ''
|
|
currentRender = undefined
|
|
return res
|
|
}
|
|
}
|
|
|
|
md.use(container, 'demo', { render })
|
|
}
|
|
|
|
function parseCodeMapping(
|
|
tokens: Token[],
|
|
index: number,
|
|
cb?: (token: Token, tokens: Token[], index: number) => void,
|
|
) {
|
|
const codeMap: Record<string, string> = {}
|
|
for (
|
|
let i = index + 1;
|
|
!(tokens[i].nesting === -1
|
|
&& tokens[i].type === 'container_demo_close');
|
|
++i
|
|
) {
|
|
const token = tokens[i]
|
|
if (token.type === 'fence') {
|
|
codeMap[normalizeAlias(token.info)] = token.content.trim()
|
|
cb?.(token, tokens, i)
|
|
}
|
|
}
|
|
return codeMap
|
|
}
|
|
|
|
function getContainerMeta(info: string): DemoMeta {
|
|
const [, type, raw] = (info.trim().slice(4).trim() || '').match(INFO_RE) || []
|
|
const { attrs } = resolveAttrs(raw)
|
|
return {
|
|
url: '',
|
|
type: (type || 'normal') as DemoMeta['type'],
|
|
...attrs,
|
|
}
|
|
}
|