imp(cetra) better plugin handling; ignore components within templates; docs

This commit is contained in:
Benjamin Wegener
2024-01-16 20:11:59 +00:00
parent 687042b24a
commit d6f72d32b8

View File

@@ -15,6 +15,8 @@ class Cetra extends Emitter {
INVALID_PLUGIN_TYPE: 'INVALID_PLUGIN_TYPE', INVALID_PLUGIN_TYPE: 'INVALID_PLUGIN_TYPE',
INVALID_COMMAND: 'INVALID_COMMAND', INVALID_COMMAND: 'INVALID_COMMAND',
COMMAND_NOT_FOUND: 'COMMAND_NOT_FOUND', COMMAND_NOT_FOUND: 'COMMAND_NOT_FOUND',
PLUGIN_NOT_FOUND: 'PLUGIN_NOT_FOUND',
REGISTERED_PLUGIN_MISSING_EXEC: 'REGISTERED_PLUGIN_MISSING_EXEC',
}; };
// class static globals // class static globals
static #instances = new Map(); static #instances = new Map();
@@ -157,6 +159,7 @@ class Cetra extends Emitter {
const entityKey = Object.keys(Cetra.#entityTypes).find((key) => { const entityKey = Object.keys(Cetra.#entityTypes).find((key) => {
return Cetra.#entityTypes[key] == entityType; return Cetra.#entityTypes[key] == entityType;
}); });
if (!entityKey) { if (!entityKey) {
Logger.error(Cetra.#ERRORS.INVALID_ENTITY_TYPE, entityType); Logger.error(Cetra.#ERRORS.INVALID_ENTITY_TYPE, entityType);
return false; return false;
@@ -178,7 +181,7 @@ class Cetra extends Emitter {
throw new TypeError(Cetra.#ERRORS.INVALID_ENTITY); throw new TypeError(Cetra.#ERRORS.INVALID_ENTITY);
} }
if (typeof fn == 'function') { if (Cetra.#isValidPlugin(fn)) {
if (!(fn instanceof CetraPlugin)) { if (!(fn instanceof CetraPlugin)) {
if (fn.name) { if (fn.name) {
Registry.unregister(uniqueId, this.#secretKey); Registry.unregister(uniqueId, this.#secretKey);
@@ -209,10 +212,14 @@ class Cetra extends Emitter {
if (typeof fn === 'undefined') return false; if (typeof fn === 'undefined') return false;
switch (entityType) { switch (entityType) {
case 'Plugin': case 'Plugin':
return fn instanceof CetraPlugin || typeof fn === 'function'; return Cetra.#isValidPlugin(fn);
case 'Command': case 'Command':
return typeof fn === 'string';
case 'Type': case 'Type':
return typeof fn === 'function'; return (
typeof fn === 'function' &&
fn.constructor.name === 'Function'
);
default: default:
return false; return false;
} }
@@ -353,13 +360,20 @@ class Cetra extends Emitter {
* @param {string} identifier - The identifier of the plugin. * @param {string} identifier - The identifier of the plugin.
* @returns {Promise<CetraPlugin>|Function|Object|undefined} - The plugin object or a promise that resolves to the plugin object. Returns undefined if the plugin is not found or if the identifier is not a string. * @returns {Promise<CetraPlugin>|Function|Object|undefined} - The plugin object or a promise that resolves to the plugin object. Returns undefined if the plugin is not found or if the identifier is not a string.
*/ */
getPlugin(identifier) { #getPlugin(identifier) {
if (!(identifier instanceof String)) { if (typeof identifier != 'string') {
Logger.error(Cetra.#ERRORS.INVALID_IDENTIFIER, identifier); Logger.error(
Cetra.#ERRORS.INVALID_IDENTIFIER,
identifier,
typeof identifier
);
return undefined; return undefined;
} }
if (!this.#internals.get('plugins').has(identifier)) return undefined; if (!this.#internals.get('plugins').has(identifier)) {
Logger.error(Cetra.#ERRORS.PLUGIN_NOT_FOUND, identifier);
return undefined;
}
const plugin = this.#internals.get('plugins').get(identifier); const plugin = this.#internals.get('plugins').get(identifier);
@@ -444,6 +458,40 @@ class Cetra extends Emitter {
}); });
} }
/**
* Checks if a plugin is a valid Cetra plugin.
* @private
* @param {Function} klass - The plugin to be checked.
* @returns {boolean} - Returns true if the plugin is a valid Cetra plugin, otherwise false.
*/
static #isValidPlugin(klass) {
let isInstance = false,
isClass = false,
isFunction = false;
if (
klass instanceof CetraPlugin ||
klass.constructor.name != 'Function'
) {
isInstance = true;
} else if (typeof klass === 'function') {
isFunction = true;
try {
isInstance = new klass() instanceof CetraPlugin;
} catch (error) {
return false;
}
if (!isInstance) {
isClass = Object.prototype.isPrototypeOf.call(
CetraPlugin.prototype,
klass.prototype
);
}
}
return isInstance || isClass || isFunction;
}
/** /**
* Initializes a plugin by registering it with the Cetra registry. * Initializes a plugin by registering it with the Cetra registry.
* *
@@ -459,16 +507,15 @@ class Cetra extends Emitter {
}); });
try { try {
if (!['function', 'object'].includes(typeof klass)) if (!Cetra.#isValidPlugin(klass)) {
throw new TypeError( if (klass.plugin) {
'plugin is not of type function or object or instance of a CetraPlugin (child) class' Registry.unregister(uniqueId, this.#secretKey);
); return this.#initPlugin(klass.plugin);
if (klass instanceof CetraPlugin) {
if (typeof klass.register === 'function') {
return klass.register(uid);
} }
if (typeof klass == 'string' && this.#isUrl(klass))
return klass;
throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE); throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE);
} }
@@ -480,6 +527,9 @@ class Cetra extends Emitter {
} }
} }
if (typeof klass.register === 'function')
return klass.register(uid);
// what else could it be? // what else could it be?
if (typeof klass !== 'object') if (typeof klass !== 'object')
throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE); throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE);
@@ -530,7 +580,9 @@ class Cetra extends Emitter {
* @param {boolean} [recursive=true] - Indicates whether to recursively process child nodes. * @param {boolean} [recursive=true] - Indicates whether to recursively process child nodes.
*/ */
#readComponents() { #readComponents() {
let comps = this.queryElements('', { _parent: this.#root }); let comps = this.#queryElements('', {
_parent: this.#root,
});
if (!comps || comps.length == 0) if (!comps || comps.length == 0)
return Logger.log('no components found (yet)'); return Logger.log('no components found (yet)');
@@ -539,7 +591,7 @@ class Cetra extends Emitter {
comp.matches( comp.matches(
`[data-${this.#internals `[data-${this.#internals
.get('settings') .get('settings')
.get('selector')}-id]` .get('selector')}-id]:not(template)`
) )
) )
continue; continue;
@@ -589,7 +641,7 @@ class Cetra extends Emitter {
* @param {string} selector - The selector string to query the DOM with. * @param {string} selector - The selector string to query the DOM with.
* @returns {NodeList} A NodeList of matching DOM elements. * @returns {NodeList} A NodeList of matching DOM elements.
*/ */
queryElements(attributes = '', options = {}) { #queryElements(attributes = '', options = {}) {
return Array.from( return Array.from(
(options._parent || document).querySelectorAll( (options._parent || document).querySelectorAll(
options.selector || this.#selectors(attributes) options.selector || this.#selectors(attributes)
@@ -597,7 +649,18 @@ class Cetra extends Emitter {
); );
} }
queryElement(attributes = '', options = {}) { /**
* Finds and returns the first element that matches the specified selector.
* If no selector is provided, it uses the selectors generated from the attributes.
*
* @param {string} attributes - The attributes used to generate the selectors.
* @param {Object} options - Additional options for querying the element.
* @param {Element} options._parent - The parent element to search within. Defaults to the document.
* @param {string} options.selector - The specific selector to use for querying the element.
*
* @returns {Element|null} The first element that matches the selector, or null if no match is found.
*/
#queryElement(attributes = '', options = {}) {
return (options._parent || document).querySelector( return (options._parent || document).querySelector(
options.selector || this.#selectors(attributes) options.selector || this.#selectors(attributes)
); );
@@ -641,12 +704,10 @@ class Cetra extends Emitter {
comp.getAttribute( comp.getAttribute(
`data-${this.#internals.get('settings').get('selector')}` `data-${this.#internals.get('settings').get('selector')}`
) ?? ) ??
comp.getAttribute(this.#internals.get('settings').get('selector')); comp.getAttribute(
this.#internals.get('settings').get('selector')
if (typeof compType == 'undefined' || !compType) { ) ??
Logger.error(Cetra.#ERRORS.INVALID_COMPONENT_TYPE); 'BaseComponent';
return null;
}
let compData = new Map([ let compData = new Map([
[ [
@@ -771,8 +832,6 @@ class Cetra extends Emitter {
*/ */
handleMutation(mutations, _observer) { handleMutation(mutations, _observer) {
if (_observer != this) return; if (_observer != this) return;
console.log('ITS HAPPENING', mutations, _observer);
} }
/** /**
@@ -786,75 +845,21 @@ class Cetra extends Emitter {
#registerPlugins(plugins) { #registerPlugins(plugins) {
if (!Array.isArray(plugins)) throw new Error('plugins is not an array'); if (!Array.isArray(plugins)) throw new Error('plugins is not an array');
for (const [identifier, plugin] of plugins) { for (const [identifier, plugin] of plugins) {
this.addPlugin(identifier, plugin); this.#addEntity('Plugin', identifier, plugin);
// this.addPlugin(identifier, plugin);
} }
} }
/** registerCommands(commands, identifier) {
* Registers a plugin with the specified name and options. if (!Array.isArray(commands))
* @memberof Cetra throw new TypeError(Cetra.#ERRORS.INVALID_COMMANDS_TYPE);
* @instance
* @method
* @param {string} name - The name of the plugin.
* @param {object} options - The options for the plugin.
*/
addPlugin(identifier, plugin) {
const uniqueId = Registry.register({
key: this.#secretKey,
obj: plugin,
parent: this.#identifier,
});
try { for (const command of commands) {
if (!identifier) { if (typeof command != 'string') {
Logger.error(Cetra.#ERRORS.INVALID_IDENTIFIER); Logger.error(Cetra.#ERRORS.INVALID_COMMAND, command);
Registry.unregister(uniqueId, this.#secretKey); continue;
return;
} }
this.#addEntity('Command', command, identifier);
if (this.#internals.get('plugins').has(identifier)) {
Registry.unregister(uniqueId, this.#secretKey);
Registry.register({
key: this.#secretKey,
obj: plugin,
parent: this.#identifier,
});
}
if (!plugin) throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE);
if (typeof plugin === 'string') {
if (!this.#isUrl(plugin))
throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_URL);
Logger.info(`Plugin ${identifier} is loaded from ${plugin}`);
this.#internals.get('plugins').set(identifier, {
plugin,
key: uniqueId,
parent: this.#identifier,
});
return;
}
if (!['function', 'object'].includes(typeof plugin))
throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE);
const pluginInstance = this.#initPlugin(plugin);
if (!pluginInstance)
throw new TypeError(Cetra.#ERRORS.INVALID_PLUGIN_TYPE);
Registry.register({
obj: pluginInstance,
key: this.#secretKey,
parent: this.#identifier,
});
this.#internals.get('plugins').set(identifier, pluginInstance);
this.emit('onAfterRegisterPlugin', pluginInstance);
} catch (err) {
Logger.error(err);
Registry.unregister(uniqueId, this.#secretKey);
} }
} }
@@ -867,7 +872,10 @@ class Cetra extends Emitter {
* @param {any} e - The event object. * @param {any} e - The event object.
* @throws {Error} If the component with the given ID is not found. * @throws {Error} If the component with the given ID is not found.
*/ */
exec(componentId, update = { command, args, evt }) { exec(
componentId,
update = { command: 'none', args: [], evt: new CustomEvent('empty') }
) {
if (Array.isArray(update.args)) update.args = []; if (Array.isArray(update.args)) update.args = [];
const comp = this.#internals.get('compIndex').get(componentId); const comp = this.#internals.get('compIndex').get(componentId);
@@ -880,22 +888,44 @@ class Cetra extends Emitter {
return; return;
} }
this.#internals const command = this.#internals.get('commands').get(update.command);
.get('commands')
.get(update.command) if (!command)
.forEach((command) => { return Logger.error(
if (!command.exec) { Cetra.#ERRORS.COMMAND_NOT_FOUND,
if (typeof command != 'function' || !command.name) { update.command,
Logger.error( update
);
if (typeof command !== 'string') {
if (typeof command.constructor.name !== 'Function') {
return Logger.error(
Cetra.#ERRORS.INVALID_COMMAND, Cetra.#ERRORS.INVALID_COMMAND,
update.command update.command
); );
} }
command(comp, update);
return command(comp, update);
} }
command.exec(comp, update); const plugin = this.#getPlugin(command);
});
if (!plugin)
return Logger.error(
Cetra.#ERRORS.PLUGIN_NOT_FOUND,
update.command,
update,
plugin
);
if (!plugin.exec)
return Logger.error(
Cetra.#ERRORS.REGISTERED_PLUGIN_MISSING_EXEC,
update.command,
update,
plugin
);
plugin.exec(comp, update);
} }
} }