You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Svelte中Shiki插件行高亮与代码块标题兼容问题修复

修复Shiki代码块行高亮与标题共存问题

原代码的核心问题是当meta中同时包含行高亮规则和代码标题时,解析逻辑会误将整个meta判定为标题,清空meta导致行高亮失效。以下是修复后的完整代码:

import { parse } from 'node-html-parser';
import { getHighlighter } from 'shiki';

const THEME = 'github-dark';

/**
 * 返回将大括号和反引号替换为HTML实体的代码
 * @param {string} code - 待转义的代码
 * @returns {string} - 转义后的HTML
 */
function escapeHtml(code) {
    return code.replace(
        /[{}`]/g,
        (character) => ({
            '{': '{',
            '}': '}',
            '`': '`'
        })[character],
    );
}

/**
 * 返回要高亮的行号数组
 * @param {string} rangeString - 待解析的范围字符串(例如 {1,3-5,8})
 * @returns {number[]}
 */
function rangeParser(rangeString) {
    const result = [];
    const ranges = rangeString.split(',');
    ranges.forEach((element) => {
        if (element.indexOf('-') === -1) {
            const num = parseInt(element.trim(), 10);
            if (!isNaN(num)) result.push(num);
        } else {
            const limits = element.split('-').map(s => parseInt(s.trim(), 10));
            const start = limits[0];
            const end = limits[1];
            if (!isNaN(start) && !isNaN(end)) {
                for (let i = start; i <= end; i += 1) {
                    result.push(i);
                }
            }
        }
    });
    return result;
}

/**
 * @param html {string} - 待处理的HTML
 * @returns {string} - 可聚焦的HTML
 */
function makeFocussable(html) {
    const root = parse(html);
    const pre = root.querySelector('pre');
    if (pre) pre.setAttribute('tabIndex', '0');
    return root.toString();
}

/**
 * @param code {string} - 待高亮的代码
 * @param lang {string} - 代码语言
 * @param meta {string} - 代码元数据
 * @returns {string} - 已高亮的HTML
 */
async function highlighter(code, lang, meta) {
    const shikiHighlighter = await getHighlighter({
        theme: THEME,
    });

    let html;
    let blockName = '';
    let highlightMeta = '';

    // 先解析代码标题(括号包裹的部分)
    if (meta) {
        const titleMatch = meta.match(/\((.*?)\)$/);
        if (titleMatch) {
            blockName = titleMatch[1].trim();
            // 移除标题部分,保留剩余的高亮规则
            meta = meta.replace(titleMatch[0], '').trim();
        }
        // 解析行高亮规则
        const highlightMatch = meta.match(/\{([\d,-]+)\}/);
        if (highlightMatch) {
            highlightMeta = highlightMatch[1];
        }
    }

    // 生成高亮HTML
    const shikiOptions = { lang };
    if (highlightMeta) {
        const highlightLines = rangeParser(highlightMeta);
        shikiOptions.lineOptions = highlightLines.map(line => ({
            line,
            classes: ['highlight-line'],
        }));
    }

    html = shikiHighlighter.codeToHtml(code, shikiOptions);
    html = makeFocussable(html);

    // 添加代码标题
    if (blockName) {
        html = `<div class="code-block-title">${blockName}</div>${html}`;
    }

    return escapeHtml(html);
}

export default highlighter;

关键修改点说明:

  • 重构meta解析逻辑:先匹配末尾的括号标题,提取后从meta中移除,再解析剩余的行高亮规则,确保两者都能被识别
  • 修复escapeHtml函数:正则添加反引号`,并修正替换对象的键值,正确转义反引号
  • 增强rangeParser鲁棒性:添加空值和非数字判断,避免无效行号导致错误
  • 优化代码结构:将Shiki配置项单独提取,逻辑更清晰,移除原代码中无用的blockName冗余语句
  • 兼容多种meta组合:支持仅标题、仅高亮、两者共存三种场景

内容的提问来源于stack exchange,提问作者Лео

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 00:37:49