美文网首页
实现一个简单的 markdown 转译器

实现一个简单的 markdown 转译器

作者: 梦想成真213 | 来源:发表于2019-12-26 11:17 被阅读0次

    实现一个 markdown 转译器,就是一行一行的解析规则,就像人眼睛看的那样来操作,一行一行的看,然后匹配规则,如果是无序列表这种多行的规则,就判断如果是无序列表,就继续往下看一行,直到规则匹配完,记录匹配的位置索引,继续向下看一行,最后将解析到所有的 html 规则放到数组里面,返回即可。这里首先是根据换行将每一行的记录到数组里面,根据索引,一行行向下看,循环里面的规则,直到索引等于数组的长度就退出循环。

    主函数

    本地写一个 markdown 文件,然后读取文件的内容,传给转译器进行转译;

    const main = () => {
        const fs = require('fs');
        const path = require('path')
        const file = path.join(__dirname, './test.md');
        const source = fs.readFileSync(file, 'utf-8');
        console.log(MdtoHtml(source).join('\n'));
    }
    main();
    

    MdtoHtml 就是实现的转译函数,这个test.md 的内容如下:

    ## 这是一个标题
    ### 无序列表
    + React
    + Vue
    + webpack
    + Antd
    这一行文字里面有**加粗**
    + vue
    `代码片段`
    

    现在就先转译这几个规则吧,MdtoHtml函数只要是包装一下,为了能看到转译的效果,直接console.log

    const MdtoHtml = (source) => {
        // 判断参数类型
        if(typeof source === 'undefined' || source === null){
            throw new Error('input parameter is udefined or null')
        }
        if(typeof source !== 'string'){
            throw new Error(`input parameter type is ${Object.prototype.toString.call(source)}, not expected string`)
        }
        // 处理所有的 \n, \t, \r 为 \n
        // markdown 转译器
       const htmls = transCompiler(source.replace(/\r\n | \r/, '\n'));
       console.log(htmls);
    }
    

    接下来transCompiler函数才是最主要的,处理拿到的每一行的规则数组。

    const transCompiler = (source) => {
        const lines = source.split('\n');
        // 返回转译过的 html 标签
        return transToDocument(lines);
    }
    const transToDocument = (lines) => {
        // 记录转译的当前行索引
        let cur = 0;
    
        // 存储转译每一行的 html 标签
        let htmls = [];
    
        // 循环转译每一行的 md 规则,如果 cur === length,跳出循环,如果有匹配的规则就进行下一轮的循环;
        // 转译每一条 markdown 规则
        while(true){
            if(cur === lines.length){
                break;
            }
    
            // 当前行
            const line = lines[cur];
    
            // 处理标题
            if(block.titleReg.test(line)){
                const { index, html } = handleTitle(lines, cur);
                htmls.push(html);
                cur = index;
                continue;
            }
    
            // 转译无序列表
            if(block.ulReg.test(line)){
                const { index, html } = handleUlList(lines, cur);
                htmls.push(html);
                cur = index;
                continue;
            }
    
            // 转译单行
            const html = singleLine(line);
            htmls.push(html);
            cur++;
        }
    
        return htmls.join('\n');
    }
    

    每一个规则都独立写到一个函数中,最后返回处理的html字符串和索引即可,处理的规则根据区块元素和区段元素划分;

     // 区块元素匹配的正则
    const block = {
        // 标题
        titleReg: /^(\#{1,6}) (.+)/,
        codeReg: /.*\`(.+)\`.*/,
        boldReg: /.*\*\*(.+)\*\*.*/,
        ulReg: /^\+ (.+)/,
    }
    
    // 区段元素
    const section = {
    }
    

    处理标题的函数 handleTitle,拿到当前行,解析,索引往下加一行,表示这行处理完,继续看下一行的规则。

    const handleTitle = (lines, cur) => {
        const [ , hash, content ] = block.titleReg.exec(lines[cur]);
        const tag = `h${hash.length}`;
        const html = `<${tag}>${content}</${tag}>`;
        return {
            index: cur + 1,
            html
        }
    }
    

    处理无序列表handleUlList,看当前行是否满足无序的正则,如果满足就继续向下看,索引加1,不满足就退出循环,然后处理匹配到的行,拼接成ul,返回新的索引,和 html 字符串。

    const handleUlList = (lines, cur) => {
        let next = cur;
        while(true){
            next++;
            if(!block.ulReg.test(lines[next])){
                break;
            }
        }
        //转译拿到的 N 行
        let htmls = [];
        for(let i = cur; i < next; i++){
            const li = lines[i];
            const result = block.ulReg.exec(li);
            const html = `<li>${result[1]}</li>`
            htmls.push(html);
        }
        // 首尾加闭合标签
        htmls.unshift('<ul>');
        htmls.push('</ul>');
        return {
            index: next,
            html: htmls.join('\n')
        }
    }
    

    再就是处理单行的函数,如果单行里面有code 或加粗,直接替换:

    const singleLine = (line) => {
        return line.replace(block.codeReg, (match, code) => {
            // 单行代码
            return `<code>${code}</code>`
        }).replace(block.boldReg, (match, bold) => {
            // 单行加粗
            return `<b>${bold}</b>`
        });
    }
    

    最后这个简单的 markdown 转译器就写完了,处理的规则很少,只领会精神,全部处理的话,非常庞大,以后慢慢加吧。运行转译器,转译之后的内容如下:

    <h3>无序列表</h3>
    <ul>
    <li>React</li>
    <li>Vue</li>
    <li>webpack</li>
    <li>Antd</li>
    </ul>
    <b>加粗</b>
    <ul>
    <li>vue</li>
    </ul>
    <code>代码片段</code>
    

    github 地址:https://github.com/mxcz213/koa-test

    分支切到 md_to_html,完整代码在 md_to_html/static/js/mdtohtml.js 里面。

    相关文章

      网友评论

          本文标题:实现一个简单的 markdown 转译器

          本文链接:https://www.haomeiwen.com/subject/ccasictx.html