export default { extensions: [ { name: 'mfm', level: 'inline', start (src) { return src.match(/\$\[/)?.index }, tokenizer (src, tokens) { // regex doesn't do well finding MFM tags: it's always either too lazy // or too greedy. let level = 0 let walk = 0 while (level > 0 || walk === 0) { if (walk >= src.length) { return null } if (src[walk] + src[walk + 1] === '$[') { level++ walk++ } else if (src[walk] === ']') { level-- } walk++ } // original regex, now definitely on the correct tag const rule = /^\$\[(?[\w\d]+)(?:\.(?\S+))? (?[\S\s]+)\]/ const match = rule.exec(src.slice(0, walk)) if (match) { const token = { type: 'mfm', raw: match[0], tag: match.groups.tag, options: match.groups.options, text: match.groups.text, tokens: [], } this.lexer.inline(token.text, token.tokens) return token } }, renderer (token) { const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'rotate', 'sparkle'] if (MFM_TAGS.includes(token.tag)) { let options = [] if (token.options) { options = token.options.split(',').map((opt) => (opt.split('=').length === 2 ? `data-${opt.split('=')[0]}="${opt.split('=')[1]}"` : `data-${opt}`)) } return `${this.parser.parseInline(token.tokens)}` } return `$[${token.tag} ${this.parser.parseInline(token.tokens)}]` }, }, { name: 'escapedMfm', level: 'inline', start (src) { return src.match(/\\\$\[/) }, tokenizer (src, tokens) { if (/^\\\$\[/.exec(src)) { return { type: 'escapedMfm', raw: '\\$[' } } }, renderer (token) { return '$[' }, }, { name: 'center', level: 'block', start (src) { return src.match(/
/) }, tokenizer (src, tokens) { const rule = /^
([\S\s]*)<\/center>/ const match = rule.exec(src) if (match) { const token = { type: 'center', raw: match[0], text: match[1], tokens: [], } this.lexer.inline(token.text, token.tokens) return token } }, renderer (token) { return `
${this.parser.parseInline(token.tokens)}
\n` }, }, ], }