mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
143 lines
3.5 KiB
TypeScript
143 lines
3.5 KiB
TypeScript
import type { RuleOptions } from 'markdown-it/lib/ruler.mjs'
|
|
import type { Markdown, MarkdownEnv } from 'vuepress/markdown'
|
|
|
|
/**
|
|
* Embed rule block options
|
|
*
|
|
* 嵌入规则块选项
|
|
*
|
|
* @typeParam Meta - Metadata type / 元数据类型
|
|
*/
|
|
export interface EmbedRuleBlockOptions<Meta extends Record<string, any>> {
|
|
/**
|
|
* Embed type syntax: @[type]()
|
|
*
|
|
* 嵌入类型语法:@[type]()
|
|
*/
|
|
type: string
|
|
/**
|
|
* Token name
|
|
*
|
|
* 令牌名称
|
|
*/
|
|
name?: string
|
|
/**
|
|
* Name of the rule to insert before
|
|
*
|
|
* 要插入在其前面的规则名称
|
|
*/
|
|
beforeName?: string
|
|
/**
|
|
* Syntax pattern regular expression
|
|
*
|
|
* 语法模式正则表达式
|
|
*/
|
|
syntaxPattern: RegExp
|
|
/**
|
|
* Rule options
|
|
*
|
|
* 规则选项
|
|
*/
|
|
ruleOptions?: RuleOptions
|
|
/**
|
|
* Extract metadata from match
|
|
*
|
|
* 从匹配中提取元数据
|
|
*
|
|
* @param match - RegExp match array / 正则表达式匹配数组
|
|
* @returns Metadata object / 元数据对象
|
|
*/
|
|
meta: (match: RegExpMatchArray) => Meta
|
|
/**
|
|
* Generate content from metadata
|
|
*
|
|
* 从元数据生成内容
|
|
*
|
|
* @param meta - Metadata / 元数据
|
|
* @param content - Original content / 原始内容
|
|
* @param env - Markdown environment / Markdown 环境
|
|
* @returns Generated content / 生成的内容
|
|
*/
|
|
content: (meta: Meta, content: string, env: MarkdownEnv) => string
|
|
}
|
|
|
|
/**
|
|
* Create embed rule block
|
|
*
|
|
* 创建嵌入规则块
|
|
*
|
|
* Syntax: \@\[name]()
|
|
*
|
|
* 语法:\@\[name]()
|
|
*
|
|
* @param md - Markdown instance / Markdown 实例
|
|
* @param {EmbedRuleBlockOptions} options - Embed rule block options / 嵌入规则块选项
|
|
* @typeParam Meta - Metadata type / 元数据类型
|
|
*/
|
|
export function createEmbedRuleBlock<Meta extends Record<string, any> = Record<string, any>>(
|
|
md: Markdown,
|
|
{
|
|
type,
|
|
name = type,
|
|
syntaxPattern,
|
|
beforeName = 'import_code',
|
|
ruleOptions = { alt: ['paragraph', 'reference', 'blockquote', 'list'] },
|
|
meta,
|
|
content,
|
|
}: EmbedRuleBlockOptions<Meta>,
|
|
): void {
|
|
const MIN_LENGTH = type.length + 5
|
|
const START_CODES = [64, 91, ...type.split('').map(c => c.charCodeAt(0))]
|
|
|
|
md.block.ruler.before(
|
|
beforeName,
|
|
name,
|
|
(state, startLine, endLine, silent) => {
|
|
const pos = state.bMarks[startLine] + state.tShift[startLine]
|
|
const max = state.eMarks[startLine]
|
|
|
|
// return false if the length is shorter than min length
|
|
// 如果长度小于最小长度,返回 false
|
|
if (pos + MIN_LENGTH > max)
|
|
return false
|
|
|
|
// check if it's matched the start
|
|
// 检查是否匹配开始
|
|
for (let i = 0; i < START_CODES.length; i += 1) {
|
|
if (state.src.charCodeAt(pos + i) !== START_CODES[i])
|
|
return false
|
|
}
|
|
|
|
// check if it's matched the syntax
|
|
// 检查是否匹配语法
|
|
const content = state.src.slice(pos, max)
|
|
const match = content.match(syntaxPattern)
|
|
if (!match)
|
|
return false
|
|
|
|
// return true as we have matched the syntax
|
|
// 返回 true 表示已匹配语法
|
|
/* istanbul ignore if -- @preserve */
|
|
if (silent)
|
|
return true
|
|
|
|
const token = state.push(name, '', 0)
|
|
|
|
token.meta = meta(match)
|
|
token.content = content
|
|
token.map = [startLine, startLine + 1]
|
|
|
|
state.line = startLine + 1
|
|
|
|
return true
|
|
},
|
|
ruleOptions,
|
|
)
|
|
|
|
md.renderer.rules[name] = (tokens, index, _, env: MarkdownEnv) => {
|
|
const token = tokens[index]
|
|
token.content = content(token.meta, token.content, env)
|
|
return token.content
|
|
}
|
|
}
|