imp(cetra) better plugin handling; ignore components within templates; docs
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user