js压缩代码后怎么生成source map_??markdown生成导航? '[toc]'足矣

6fd0c75e73342a70b847ea844a3f6ec4.png
快速食用地址: https:// github.com/ahungrynoob/ showdown-toc

背景

什么是toc? table of contents 是markdown中的导航信息,能够通过heading和锚点快速定位。

最近在开发个人博客的时候,希望有一个生成目录导航的功能,目的是做一个像语雀右上角一样的导航栏:

0230cbc7d819ae5cd0ef86ffb78be257.png


使用的markdown渲染引擎是showdown,找了半天,只找到一个jquery写的toc插件,也不支持nodejs。(很好,又是一个造轮子,开源,刷福报的好机会!)
于是就自己做了一个插件,支持两大功能:

  1. 会把文章中所有的heading信息,通过闭包的形式传达到上层域
  2. markdown中写[toc],即可生成toc到markdown的相应位置中。

效果:

使用前:

52f66f126243a43e629f046fc2136a99.png

使用后:

4a0dfd8617c76a4872c2a0b0a1b2049b.png

开发过程

汲取和熟悉

首先阅读showdown的wiki,了解extension机制和写法。了解到以下几点:

  1. extension 分为 lang 和 output两种
  2. lang 的extension会在markdown一开始的时候(normalize之后)就执行,output会在最后执行。
  3. 作者还很热心的指出了一些坑(递归调用,不要修改converter):https://github.com/showdownjs/showdown/wiki/extensions#filter-property

思路

老的思路:自己写一个lang插件,在一开始引擎读md的时候,就去收集信息,结果证明:❌
原因: 实践过程中,showdown有缓存机制,只会读取一遍md内容,之后读的都是html的内容,就无法收集toc的元信息了。

新思路:在output阶段中解析html的h1到h6标签,收集heading信息,并替代[toc]的占位符,输出到html中,结果证明:✅

步骤以及代码:

整个代码的核心是这个正则:/(<h([1-6]).*?id="([^"]*?)".*?>(.+?)</h[1-6]>)|(<p>[toc]</p>)/g;
这个正则,获取了h1-h6的标题信息以及toc的占位符的位置和次数

第一步:

在这里,showdown-toc会去寻找并按出现次序收集tocItem[toc]

// find and collect all headers and [toc] node;
        const collection: MetaInfo[] = [];
        source.replace(regex, (wholeMatch, _, level, anchor, text) => {
          if (wholeMatch === '<p>[toc]</p>') {
            collection.push({ type: 'toc' });
          } else {
            text = text.replace(/<[^>]+>/g, '');
            const tocItem = {
              anchor,
              level: Number(level),
              text,
            };
            // 如果传了闭包的数组参数toc,就会把标题信息推入
            if (toc) {
              toc.push(tocItem);
            }
            collection.push({
              type: 'header',
              ...tocItem,
            });
          }
          return '';
        });

tocItem长这样:

type TocItem = {
  anchor: string; // 锚点
  level: number;  // 标题级别
  text: string;  // 标题内容
};

第二步

这里,出现[toc]之后,会收集这个[toc]到下个[toc]之间的标题信息

// calculate toc info
        const tocCollection: TocItem[][] = [];
        collection.forEach(({ type }, index) => {
          if (type === 'toc') {
            if (collection[index + 1] && collection[index + 1].type === 'header') {
              const headers = [];
              const { level: levelToToc } = collection[index + 1] as TocItem;
              for (let i = index + 1; i < collection.length; i++) {
                if (collection[i].type === 'toc') break;
                const { level } = collection[i] as TocItem;
                if (level === levelToToc) {
                  headers.push(collection[i] as TocItem);
                }
              }
              tocCollection.push(headers);
            } else {
              tocCollection.push([]);
            }
          }
        });

第三步:

这个阶段,会把source中的showdown给我们生成的<p>[toc]</p>标签替换成我们生成toc标签,也就是olli标签啦。然后把处理后的source返回给showdown就好了。整个插件也就完成了。

// replace [toc] node in source
        source = source.replace(/<p>[toc]</p>[n]*/g, () => {
          const headers = tocCollection.shift();
          if (headers && headers.length) {
            const str = `<ol>${headers
              .map(({ text, anchor }) => `<li><a href="#${anchor}">${text}</a></li>`)
              .join('')}</ol>n`;
            return str;
          }
          return '';
        });

总结

整个插件很简单,但是中间也遇到了不少坑,出现过思路的碰撞。实现的主要步骤逻辑如下:

  1. 寻找和收集h1-h6以及[toc]的出现次数和次序
  2. 计算两次[toc]之间的标题
  3. 用我们自己生成toc标签去替换<p>[toc]</p>占位标签,输出成html字符串。

欢迎pr

项目地址:https://github.com/ahungrynoob/showdown-toc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值