"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtensionBuilder = void 0;
const tslib_1 = require("tslib");
const orderedmap_1 = tslib_1.__importDefault(require("orderedmap")); // eslint-disable-line import/no-extraneous-dependencies
const prosemirror_inputrules_1 = require("prosemirror-inputrules");
const prosemirror_keymap_1 = require("prosemirror-keymap");
var Priority;
(function (Priority) {
    Priority[Priority["Highest"] = 1000000] = "Highest";
    Priority[Priority["VeryHigh"] = 100000] = "VeryHigh";
    Priority[Priority["High"] = 10000] = "High";
    Priority[Priority["Medium"] = 1000] = "Medium";
    Priority[Priority["Low"] = 100] = "Low";
    Priority[Priority["VeryLow"] = 10] = "VeryLow";
    Priority[Priority["Lowest"] = 0] = "Lowest";
})(Priority || (Priority = {}));
const DEFAULT_PRIORITY = Priority.Medium;
class ExtensionBuilder {
    static createContext() {
        return new Map();
    }
    /* eslint-disable @typescript-eslint/member-ordering */
    static Priority = Priority;
    Priority = ExtensionBuilder.Priority;
    /* eslint-enable @typescript-eslint/member-ordering */
    #confMdCbs = [];
    #nodeSpecs = {};
    #markSpecs = {};
    #plugins = [];
    #actions = [];
    context;
    constructor(context) {
        this.context = context ?? ExtensionBuilder.createContext();
    }
    use(extension, ...params) {
        extension(this, ...params);
        return this;
    }
    configureMd(cb, params = {}) {
        this.#confMdCbs.push({
            cb,
            params: {
                text: params.text ?? true,
                markup: params.markup ?? true,
            },
        });
        return this;
    }
    addNode(name, cb) {
        if (this.#nodeSpecs[name]) {
            throw new Error(`ProseMirror node with this name "${name}" already exist`);
        }
        this.#nodeSpecs[name] = { name, cb };
        return this;
    }
    addMark(name, cb, priority = DEFAULT_PRIORITY) {
        if (this.#markSpecs[name]) {
            throw new Error(`ProseMirror mark with this name "${name}" already exist`);
        }
        this.#markSpecs[name] = { name, cb, priority };
        return this;
    }
    addPlugin(cb, priority = DEFAULT_PRIORITY) {
        this.#plugins.push({ cb, priority });
        return this;
    }
    addKeymap(cb, priority = DEFAULT_PRIORITY) {
        this.#plugins.push({ cb: (...args) => (0, prosemirror_keymap_1.keymap)(cb(...args)), priority });
        return this;
    }
    addInputRules(cb, priority = DEFAULT_PRIORITY) {
        this.#plugins.push({ cb: (...args) => (0, prosemirror_inputrules_1.inputRules)(cb(...args)), priority });
        return this;
    }
    addAction(name, cb) {
        if (this.#actions.some(([actionName]) => actionName === name)) {
            throw new Error(`[Markdown Wysiwyg Editor] action with this name "${name}" already exist`);
        }
        this.#actions.push([name, cb]);
        return this;
    }
    build() {
        const confMd = this.#confMdCbs.slice();
        const nodes = { ...this.#nodeSpecs };
        const marks = { ...this.#markSpecs };
        const plugins = this.#plugins.slice();
        const actions = this.#actions.slice();
        return {
            configureMd: (md, parserType) => confMd.reduce((pMd, { cb, params }) => {
                if (parserType === 'text' && params.text) {
                    return cb(pMd);
                }
                if (parserType === 'markup' && params.markup) {
                    return cb(pMd);
                }
                return pMd;
            }, md),
            nodes: () => {
                let map = orderedmap_1.default.from({});
                for (const { name, cb } of Object.values(nodes)) {
                    map = map.addToEnd(name, cb());
                }
                return map;
            },
            marks: () => {
                // The order of marks in schema is important when serializing pm-document to DOM or markup
                // https://discuss.prosemirror.net/t/marks-priority/4463
                const sortedMarks = Object.values(marks).sort((a, b) => b.priority - a.priority);
                let map = orderedmap_1.default.from({});
                for (const { name, cb } of sortedMarks) {
                    map = map.addToEnd(name, cb());
                }
                return map;
            },
            plugins: (deps) => {
                return plugins
                    .sort((a, b) => b.priority - a.priority)
                    .reduce((acc, { cb }) => {
                    const res = cb(deps);
                    if (Array.isArray(res))
                        acc.push(...res);
                    else
                        acc.push(res);
                    return acc;
                }, []);
            },
            actions: (deps) => actions.reduce((obj, [name, cb]) => {
                obj[name] = cb(deps);
                return obj;
            }, {}),
        };
    }
}
exports.ExtensionBuilder = ExtensionBuilder;
//# sourceMappingURL=ExtensionBuilder.js.map
