showdownjs / showdown

A bidirectional Markdown to HTML to Markdown converter written in Javascript
http://www.showdownjs.com/
MIT License
14.1k stars 1.57k forks source link

Incorrect Regex in "Add default classes for each HTML element" Wiki Page #1005

Open alifyasa opened 3 months ago

alifyasa commented 3 months ago

Description

The wiki example contains incorrect regex patterns, leading to unexpected results.

Example

Let's say we are trying to convert mdText with the following default classes:

const mdText = `
This is a paragraph

\`\`\`
This is a code block.
\`\`\`
`
const classMap = {
    p: 'text-sm',
}

If we use the code in the wiki example, the <pre> tag would be incorrectly replaced.

const oldBindings = Object.keys(classMap)
    .map(key => ({
        type: 'output',
        regex: new RegExp(`<${key}(.*)>`, 'g'),
        replace: `<${key} class="${classMap[key]}" $1>`
    }));
const oldConv = new Showdown.Converter({
    extensions: [...oldBindings]
});
console.log(oldConv.makeHtml(mdText))
// Output is:
// <p class="text-sm" >This is a paragraph</p>
// <p class="text-sm" re><code>This is a code block.
// </code></pre>

Notice that <pre> is replaced by <p class="text-sm" re>.

Suggestion

I suggest using this regex pattern:

const newBindings = Object.keys(classMap)
    .map(key => ({
        type: 'output',
        regex: new RegExp(`(?:<${key}>)|(?:<${key} (.*)>)`, 'g'),
        replace: `<${key} class="${classMap[key]}" $1>`
    }));
const newConv = new Showdown.Converter({
    extensions: [...newBindings]
})
console.log(newConv.makeHtml(mdText))
// Output is:
// <p class="text-sm" >This is a paragraph</p>
// <pre><code>This is a code block.
// </code></pre>

This new regex pattern ensures proper replacement without affecting other HTML tags.