猫史档案馆


NomenBox:快速集成,快速调用工具与插件,拒绝代码污染与不匹配

用户:NomenNomen查看:1 回复:3 评论:1 创建时间:2023-08-14T12:19:28


这里是没有开场白的nOmEn,相信在大家开发的过程中会遇到想要使用某一个别人写的功能,但是放到自己的地图,要么会出现污染行为,出现报错,要么就是与自己的预期不匹配,从而还需要改写这段代码才能正常运行

我为大家带来了一种全新的协议:NomenBoxPlugin,这个协议允许我们可以快捷地与集成化的插件交流,插件也可以监听事件,从而带来更好的开发体验

Nomen是一个萌新,代码可能存在错误,欢迎回帖纠正!同时这只是个初代版本,是我随便瞎写的,我技术不够好,还希望大家能够鼓励一下我呀!

示例:

const config = {
    db: null,                          // 一个db标准,插件将可以读取
    warning: true,                     // 是否输出警告
    info: true,                        // 是否输出信息
    plugins: require("./plugins.js")   // 插件的数组 plugin[]
}

let box = new NomenBox(config);
box.init();
box.onInit(async ({ shareToPlugin, noticeToPlugin, sendToPlugin }) => {
    shareToPlugin({
        data: "一些数据,插件将可以读取"
    });
    noticeToPlugin("nomina:mynewPlugin", "一些信息,该id的插件将会收到这条提醒");
    sendToPlugin("nomina:mynewPlugin", "一些信息,该id的插件将会收到这些信息")
    box.run(function ({ plug, noticeToPlugin }) {
        console.log(JSON.stringify(box.pluginInfo("nomina:mynewPlugin"))) // 输出插件信息
        let mnp = plug("nomina:mynewPlugin"); // 导入插件
        mnp.send("world"); // 调用插件,这个时候会输出: hello world! 我是一个方法,可以被使用者调用
        noticeToPlugin("nomina:mynewPlugin", "再次提醒!")
    })
});

假设的插件集合中(注意,这里的示例代码会引用NomenBox类本身,可能需要进行上下文调整):

// plugin.js
module.exports = [
    {
        id: "mynewPlugin",
        namespace: "nomina",
        version: "0.0.1",
        events: {
            "oninit": () => console.log("NomenBox载入的时候会调用,等价于box.onInit,与用户同时监听到事件发生"),
            "plugin:onload": (data) => {
                console.log("插件载入的时候会调用");
                // 提示,一个更高级的用法是获取当前环境下的NomenBox实例
                console.log(`你猜怎么着,我其实知道你的NomenBox实例,它就是: ${data[NomenBox.nthis]}`);
                console.log(`同时,我也可以获取我自己: ${data.plugin}`)
            },
            "notification:nomina:mynewPlugin": () => console.log("我收到了你使用noticeToPlugin发送的提醒")
        },
        exports: () => {
            return {
                send: (data) => { console.log(`hello ${data}! 我是一个方法,可以被使用者调用,使用者只需要plug("nomina:mynewPlugin").send("world")就可以了`) }
            }
        },
        defaults: (data) => { console.log(`我收到了你使用sendToPlugin发送的数据:${data}`) }
    }
]

本体:

class NomenBox {
    static get defaultConfig() {
        return {
            db: null,
            warning: true,
            info: true,
            pluginShared: {},
            plugins: []
        }
    }
    static get errorMessages() {
        return {
            "-3": "事件订阅器必须是一个回调函数",
            "-2": "事件名称必须是一个有效字符串",
            "-1": "配置是必须的",
            "1": "尝试导入一个不存在的插件时发生错误"
        }
    }
    static get nthis() {
        return this._nthis ? this._nthis : (this._nthis = Symbol("nthis"), this._nthis)
    }
    static getInstance() {
        return this.__instance;
    }

    /* Constructor */
    constructor(config) {
        // console.log(this.constructor)
        if (this.constructor.getInstance()) return this.constructor.getInstance();
        if (!config) throw this._genError("-1");
        this.assignConfig(config);
        this._events = {};
        this._plugCache = [];
        this._plugins = [];
        this._initPlugins(config.plugins);
        this.constructor.__instance = this;
    }

    /* Methods */
    async init() {
        await this._initDatabase(); // TODO
        this._triggerEvent("oninit", {
            [this.constructor.nthis]: this,
            shareToPlugin: this.shareToPlugin.bind(this),
            noticeToPlugin: this.noticeToPlugin.bind(this),
            sendToPlugin: this.sendToPlugin.bind(this),
            pluginInfo: this.pluginInfo.bind(this),
        });
    }
    assignConfig(config) {
        let o = {}, defalutConfig = this.constructor.defaultConfig;
        Object.keys(defalutConfig).forEach(k => o[k] == config[k] || defalutConfig[k]);
        Object.assign(this, defalutConfig, o);
    }
    pluginInfo(id) {
        if (!this._plugins[id]) return null;
        return {
            id: this._plugins[id].id,
            namespace: this._plugins[id].namespace,
            version: this._plugins[id].version || "0.0.1",
            eventsLength: Object.keys(this._plugins[id].events).length,
            listeningEvents: Object.keys(this._plugins[id].events)
        }
    }
    shareToPlugin(...data) {
        Object.assign(this.pluginShared, ...data);
    }
    noticeToPlugin(id, msg) {
        this._triggerEvent(`notification:${id}`, msg)
    }
    sendToPlugin(id, data) {
        if (this._plugins[id] && this._plugins[id].defaults) this._plugins[id].defaults(data)
    }
    plug(id) {
        if (!this._plugins[id]) throw this._genError("1");
        if (this._plugCache[id]) return this._plugCache[id];
        if (this._plugins[id].exports) return this._plugins[id].exports({
            [this.constructor.nthis]: this,
            pluginShared: this.pluginShared
        });
    }
    run(c) {
        return c({
            [this.constructor.nthis]: this,
            plug: this.plug.bind(this),
            shareToPlugin: this.shareToPlugin.bind(this),
            noticeToPlugin: this.noticeToPlugin.bind(this),
            sendToPlugin: this.sendToPlugin.bind(this),
            pluginInfo: this.pluginInfo.bind(this),
        })
    }

    /* Event Describer */
    onInit(c) {
        this._describeEvent("oninit", c);
    }

    /* Private Methods */
    _require(path) {
        let nrequire = function (p) {
            try {
                return { module: require(p) }
            } catch {
                return { module: null }
            }
        }
        if (typeof path == "string" && path.startsWith("./") && path.endsWith(".js")) return nrequire(path)
        else if (typeof path == "string" && !path.startsWith("./")) return nrequire(`./${path}.js`)
        else if (Array.isArray(path) && path.every(v => typeof v == "string")) return path.map(v => nrequire(v));
    }
    _pluginExec(id, event, ...arg) {
        try {
            if (this._plugins[id].events && this._plugins[id].events[event]) {
                this._plugins[id].events[event](...arg);
            }
        } catch (error) {
            this._error(`插件[${id}]运行时遇到错误:${error}`)
        }
    }
    _initPlugins(plugins) {
        if (Array.isArray(plugins)) {
            plugins.forEach(p => {
                try {
                    if (p.namespace && p.id && p.id.length >= 1 && this._plugins[p.namespace + ":" + p.id] == undefined) {
                        console.log(p.namespace + ":" + p.id)
                        this._plugins[p.namespace + ":" + p.id] = p;
                        if (this._plugins[p.namespace + ":" + p.id].events) {
                            Object.keys(this._plugins[p.namespace + ":" + p.id].events).forEach(e => {
                                this._describeEvent(e, this._plugins[p.namespace + ":" + p.id].events[e]);
                            })
                        };
                        this._triggerEvent("plugin:onload", {
                            [this.constructor.nthis]: this,
                            plugin: p
                        })
                        this._info(`插件载入:[${p.namespace + ":" + p.id}]`);
                    } else if (!p.namespace && (!p.id || p.id.length <= 0)) {
                        this._warn(`存在未知的插件,无法载入,请检查你的插件集`);
                    } else if (this._plugins[p.namespace + ":" + p.id]) {
                        this._warn(`插件 ${p.namespace + ":" + p.id} 已经存在,请检查是否存在冲突`)
                    }
                } catch (error) {
                    this._error(`尝试载入插件时遇到错误:${error}`)
                }
            })
        }
    }
    async _initDatabase(arg) {
        if (!this.db) this._db = null
        else if (this.db && typeof this.db == "function") this._db = await this.db(arg)
        else if (typeof this.db == "object") this._db = this.db;
    }
    _info(msg) {
        this._logout("log", msg);
    }
    _warn(msg) {
        this._logout("warn", msg);
    }
    _error(msg) {
        this._logout("error", msg);
    }
    _logout(level, msg) {
        let lvms = {
            "log": "info",
            "warn": "warning",
            "error": "error"
        };
        if (this[lvms[level]] || level == "error") console[level](`[NomenBox ${lvms[level].toUpperCase()}] ${msg}`)
    }
    _describeEvent(event, c) {
        if (typeof event !== "string") throw this._genError("-2");
        if (!this._events[event]) this._events[event] = [];
        if (typeof c !== "function") throw this._genError("-3");
        this._events[event].push(c);
    }
    _triggerEvent(event, ...arg) {
        if (this._events[event]) this._events[event].forEach(v => v(...arg));
    }
    _genError(code) {
        let errorMessages = this.constructor.errorMessages;
        return new Error(`[NomenBox: ${code}]${errorMessages[code] ? errorMessages[code] : "error when running NomenBox"}`)
    }
}


回复

上一页1 页 / 共 1下一页
NomenNomen

照例沙发

点赞1


评论


Neural_NetworkNeural_Network

基尼泰美

点赞1


评论


伴只狗头伴只狗头

省流:插件协议)

点赞0


评论