手写一个markDown转html的plugin

205 阅读1分钟

手写一个markDown转html的plugin

  1. 创建package.json
  • npm init -y
  1. 生产环境安装webpack三大件
    "devDependencies": {
        "webpack": "^4.30.0",
        "webpack-cli": "^3.3.0",
        "webpack-dev-server": "^3.7.2"
    }
  • npm i
  1. 新建webpack.config.js
    const { resolve } = require("path");
    const MdToHtmlPlugin = require("./plugins/md-to-html-plugin");

    module.exports = {
        mode: "development",
        entry: resolve(__dirname, "src/app.js"),
        output: {
            path: resolve(__dirname, "dist"),
            filename: "app.js"
        },
        plugins: [
            new MdToHtmlPlugin({
                template: resolve(__dirname, "test.md"),
                filename: "test.html"
            })
        ]
    }
  1. 新建src/app.js plugins/md-to-html-plugin/index.js、template.html、utils.js 等文件
    // src/app.js
    // 空 只为了一个入口
    // plugins/md-to-html-plugin/index.js
    
    const { resolve } = require("path");
    const { readFileSync } = require("fs");
    const { compileHTML } = require("./utils");

    const INNER_MARK = "<!-- inner -->";

    class MdToHtmlPlugin{
        constructor ({ template, filename }) {
            if ( !template ) {
                throw new Error("The config for 'template' must be configured");
            }

            this.template = template;
            this.filename = filename ? filename : "md.html";
        }

        /**
        * 编译其实是apply中做的
        * compiler 编译器 下面有相应的钩子集合
        * 第一个参数随意,一半是插件名称
        * 第二个是回调函数 compilation下有assets 资源
        */
        apply (compiler) {
            compiler.hooks.emit.tap("md-to-html-plugin", compilation => {
                const _assets = compilation.assets;

                // 需要转换的md文件,剪切成数组替换
                const _mdContent = readFileSync(this.template, "utf-8");
                const _mdContentArr = _mdContent.split("\n");
                // 模板文件,将处理后的数据替换掉预留的插入标记INNER_MARK
                const _htmlContent = readFileSync(resolve(__dirname, "template.html"), "utf-8");

                const _outContent = _htmlContent.replace(INNER_MARK, compileHTML(_mdContentArr)) ;


                // 往资源中增加文件
                _assets[this.filename] = {
                    source () {
                        return _outContent;
                    },
                    size () {
                        return _outContent.length
                    }
                }
            })
        }
    }

    module.exports = MdToHtmlPlugin;
    // plugins/md-to-html-plugin/utils.js
    
    // 空字符串开头 空格结尾 中间必须有字符
    const reg_mark = /^(.+?)\s/;
    const reg_sharp = /^\#/;
    const reg_crossbar = /^\-/;
    const reg_number = /^\d/;

    function createTree (mdArr) {
        let _htmlPool = {};
        let _lastMark = "";
        let _key = 0;

        mdArr.forEach(mdFragment => {
            const matched = mdFragment.match(reg_mark);

            if ( matched ) {
                const mark = matched[1];
                const input = matched["input"];

                if ( reg_sharp.test(mark) ) {
                    const tag = `h${mark.length}`;
                    const tagContent = input.replace(reg_mark, "");

                    if ( _lastMark === mark ) {
                        _htmlPool[tag].tags = [..._htmlPool[tag].tags, `<${tag}>${tagContent}</${tag}>`];
                    } else {
                        _lastMark = mark;
                        _key = randomNum();
                        _htmlPool[`${tag}-${_key}`] = {
                            type: "single",
                            tags: [`<${tag}>${tagContent}</${tag}>`]
                        }
                    }
                }

                if( reg_crossbar.test(mark) ){
                    const tag = `li`;
                    const tagContent = input.replace(reg_mark, "");

                    if ( reg_crossbar.test(_lastMark) ) {
                        _htmlPool[`ul-${_key}`].tags = [..._htmlPool[`ul-${_key}`].tags, `<${tag}>${tagContent}</${tag}>`];
                    } else {
                        _lastMark = mark;
                        _key = randomNum();
                        _htmlPool[`ul-${_key}`] = {
                            type: "warp",
                            tags: [`<${tag}>${tagContent}</${tag}>`]
                        }
                    }
                }

                
                if( reg_number.test(mark) ){
                    const tag = `li`;
                    const tagContent = input.replace(reg_mark, "");

                    if ( reg_number.test(_lastMark) ) {
                        _htmlPool[`ol-${_key}`].tags = [..._htmlPool[`ol-${_key}`].tags, `<${tag}>${tagContent}</${tag}>`];
                    } else {
                        _lastMark = mark;
                        _key = randomNum();
                        _htmlPool[`ol-${_key}`] = {
                            type: "warp",
                            tags: [`<${tag}>${tagContent}</${tag}>`]
                        }
                    }
                }
            }
        });

        return _htmlPool;
    }

    function compileHTML (_mdArr) {
        const _htmlPool = createTree(_mdArr);
        let _htmlStr = "";
        let item;
        for (let key in _htmlPool) {
            item = _htmlPool[key];
            if ( item.type === "single" ) {
                item.tags.forEach(tag => {
                    _htmlStr += tag;
                })
            } else if (item.type === "wrap") {
                let _list = `<${k.split("-")[0]}>`;
                item.tags.forEach(tag => {
                    _list += tag;
                });
                _list += `</${k.split("-")[0]}>`;
                _htmlStr += _list;
            }
        }

        return _htmlStr;
    }

    function randomNum () {
        return new Date().getTime() + parseInt(Math.random() * 10000);
    }

    module.exports = {
        compileHTML,
        randomNum
    }
  1. 修改package.json scripts
    "scripts": {
        "build": "webpack"
    },