0

I am using Marked.js for parsing Markdown. The original Markdown language does not support different ordered list styles, for instance, lettered ones, like HTML. I would like to introduce this function to the parser. Below is my JavaScript code:

const customList = {
    name: "customList",
    level: "block",
    start(src) {
        return src.match(/(?:^|\n)(\s*)(-|\+|\*|\d+\.|[A-Za-z]+\.|[iIvVxX]+\.)(\s+)/)?.index;
    },
    tokenizer(src, tokens) {
        const rule = /^([^\S\r\n]*)(-|\+|\*|\d+\.|[A-Za-z]+\.|[iIvVxX]+\.)(\s+)(.*)/;
        const textRule = /^([^\S\r\n]*)(.*)/;
        const match = rule.exec(src);
        let style;
        if (match) {
            if (/-/.test(match[2])) style = "-";
            else if (/\+/.test(match[2])) style = "+";
            else if (/\*/.test(match[2])) style = "*";
            else if (/\d+\./.test(match[2])) style = "1";
            else if (/[IVX]+\./.test(match[2])) style = "I";
            else if (/[ivx]+\./.test(match[2])) style = "i";
            else if (/[A-Z]+\./.test(match[2])) style = "A";
            else if (/[a-z]+\./.test(match[2])) style = "a";
            const items = [];
            let remainingSrc = src;
            let prevIndent = match[1].length;
            let itemText = "", raw = "";
            while (remainingSrc) {
                const itemMatch = rule.exec(remainingSrc);
                const textMatch = textRule.exec(remainingSrc);
                if (itemMatch && itemMatch[1].length === prevIndent) {
                    let itemStyle;
                    if (/-/.test(itemMatch[2])) itemStyle = "-";
                    else if (/\+/.test(itemMatch[2])) itemStyle = "+";
                    else if (/\*/.test(itemMatch[2])) itemStyle = "*";
                    else if (/\d+\./.test(itemMatch[2])) itemStyle = "1";
                    else if (/[IVX]+\./.test(itemMatch[2])) itemStyle = "I";
                    else if (/[ivx]+\./.test(itemMatch[2])) itemStyle = "i";
                    else if (/[A-Z]+\./.test(itemMatch[2])) itemStyle = "A";
                    else if (/[a-z]+\./.test(itemMatch[2])) itemStyle = "a";
                    else break;
                    if (itemStyle !== style) break;
                    if (itemText) {
                        const itemTokens = [];
                        this.lexer.inlineTokens(itemText, itemTokens);
                        items.push({
                            type: "customListItem",
                            raw: raw,
                            tokens: itemTokens
                        });
                    }
                    itemText = itemMatch[4].trim();
                    raw = itemMatch[0];
                    remainingSrc = remainingSrc.slice(itemMatch[0].length + 1);
                }
                else if (textMatch && textMatch[1].length === prevIndent) {
                    itemText += "\n" + textMatch[2];
                    raw += "\n" + textMatch[0];
                    remainingSrc = remainingSrc.slice(textMatch[0].length + 1);
                }
                else break;
            }
            if (itemText) {
                const itemTokens = [];
                this.lexer.inlineTokens(itemText, itemTokens);
                items.push({
                    type: "customListItem",
                    raw: raw,
                    tokens: itemTokens
                });
            }
            const token = {
                type: "customList",
                raw: src.slice(0, src.length - remainingSrc.length),
                style: style,
                items: items
            };
            return token;
        }
    },
    renderer(token) {
        const listItems = token.items.map(item =>
            `<li>${this.parser.parseInline(item.tokens)}</li>`
        ).join('\n');
        if (token.style === "-" || token.style === "+" || token.style === "*") return `<ul>\n${listItems}\n</ul>\n`;
        else if (token.style === "1" || token.style === "I" || token.style === "i" || token.style === "A" || token.style === "a") return `<ol type="${token.style}">\n${listItems}\n</ol>\n`;
    },
    childTokens: ["items"]
};
marked.use({ extensions: [customList] });

It works normally in most cases:

Example 1

marked.parse(" 1. first\n 2. second\n 3. third")

Output

<ol type="1">
    <li>first</li>
    <li>second</li>
    <li>third</li>
</ol>

Example 2

marked.parse("A. first\nB. second\n* first\n* second")

Output

<ol type="A">
    <li>first</li>
    <li>second</li>
</ol>
<ul>
    <li>first</li>
    <li>second</li>
</ul>

However, when I try this:

marked.parse("1 + 1")

The output becomes:

<p>1</p>
<ul>
    <li>1</li>
</ul>

I expect that only the list markers appearing at the beginning of a line (surrounded by whitespaces) will be detected as a list.

I tried getting some values of the variables by adding console.log(src) in the tokenizer function. Surprisingly, I receive two outputs when I try the above case.

1 + 1
 + 1

Why is this occurring? I am sorry that I am new to JavaScript, and the code may look clumsy. If you need more information, please let me know. Thanks a lot.

2
  • 1
    Is this any different than github.com/jasny/marked-more-lists ? Commented Jun 10 at 17:22
  • 1
    @RokoC.Buljan Sorry for the late reply. I think this is exactly what I am looking for. Thank you so much. Commented Jun 11 at 5:38

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.