struct/EmoteParser.js

const Constants = require('../util/Constants');

class EmoteParser {
    /**
     * A parser to replace text with emotes.
     * @param {EmoteFetcher} fetcher - The fetcher to use the cache of.
     * @param {Object} [options={}] - Options for the parser.
     * @param {string} [options.template] - The template to be used.
     * The strings that can be interpolated are:
     * - `{link}` The link of the emote.
     * - `{name}` The name of the emote.
     * - `{size}` The size of the image.
     * - `{creator}` The channel/owner name of the emote.
     * @param {string} [options.type='markdown'] - The type of the parser.
     * Can be one of `markdown`, `html`, `bbcode`, or `plain`.
     * If the `template` option is provided, this is ignored.
     * @param {RegExp} [options.match=/:(.+?):/g] - The regular expression that matches an emote.
     * Must be a global regex, with one capture group for the emote code.
     */
    constructor(fetcher, options = {}) {
        /**
         * The emote fetcher being used.
         * @type {EmoteFetcher}
         */
        this.fetcher = fetcher;

        /**
         * The parser options.
         * @type {Object}
         */
        this.options = Object.assign({
            template: '',
            type: 'markdown',
            match: /:(.+?):/g
        }, options);

        this._validateOptions(this.options);
    }

    _validateOptions(options) {
        if (options.template && typeof options.template !== 'string') {
            throw new TypeError('Template must be a string');
        }

        if (!['markdown', 'html', 'bbcode', 'plain'].includes(options.type)) {
            throw new TypeError('Parse type must be one of `markdown`, `html`, `bbcode`, or `plain`');
        }

        if (!(options.match instanceof RegExp) || !options.match.global) {
            throw new TypeError('Match must be a global RegExp.');
        }
    }

    /**
     * Parses text.
     * @param {string} text - Text to parse.
     * @param {number} size - Size for emotes.
     * @returns {string}
     */
    parse(text, size = 0) {
        const parsed = text.replace(this.options.match, (matched, id) => {
            const emote = this.fetcher.emotes.get(id);
            if (!emote) return matched;

            const template = this.options.template || Constants.Templates[this.options.type];
            const link = emote.toLink(size);
            const res = template
                .replace(/{link}/g, link)
                .replace(/{name}/g, emote.code)
                .replace(/{size}/g, size)
                .replace(/{creator}/g, emote.ownerName || 'global');

            return res;
        });

        return parsed;
    }
}

module.exports = EmoteParser;