Skip to content
This repository has been archived by the owner on Dec 24, 2021. It is now read-only.

Commit

Permalink
Add more specific check for constructors than simply checking if it's…
Browse files Browse the repository at this point in the history
… a function when registering components in the registry (#179)

* Replaced simple function checks for actual checks that the function is a constructor of a subclass of Command in the registry loader functions

* Added isConstructor checks for groups and types as well
  • Loading branch information
Linn Dahlgren committed Jan 17, 2021
1 parent 576738b commit 59a0d8e
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 11 deletions.
20 changes: 10 additions & 10 deletions src/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const Command = require('./commands/base');
const CommandGroup = require('./commands/group');
const CommandoMessage = require('./extensions/message');
const ArgumentType = require('./types/base');
const { isConstructor } = require('./util');

/** Handles registration and searching of commands and groups */
class CommandoRegistry {
Expand Down Expand Up @@ -59,7 +60,7 @@ class CommandoRegistry {
registerGroup(group, name, guarded) {
if(typeof group === 'string') {
group = new CommandGroup(this.client, group, name, guarded);
} else if(typeof group === 'function') {
} else if(isConstructor(group, CommandGroup)) {
group = new group(this.client); // eslint-disable-line new-cap
} else if(typeof group === 'object' && !(group instanceof CommandGroup)) {
group = new CommandGroup(this.client, group.id, group.name, group.guarded);
Expand Down Expand Up @@ -118,10 +119,9 @@ class CommandoRegistry {
*/
registerCommand(command) {
/* eslint-disable new-cap */
if(typeof command === 'function') command = new command(this.client);
else if(typeof command.default === 'function') command = new command.default(this.client);
if(isConstructor(command, Command)) command = new command(this.client);
else if(isConstructor(command.default, Command)) command = new command.default(this.client);
/* eslint-enable new-cap */

if(!(command instanceof Command)) throw new Error(`Invalid command object to register: ${command}`);

// Make sure there aren't any conflicts
Expand Down Expand Up @@ -167,7 +167,7 @@ class CommandoRegistry {
registerCommands(commands, ignoreInvalid = false) {
if(!Array.isArray(commands)) throw new TypeError('Commands must be an Array.');
for(const command of commands) {
const valid = typeof command === 'function' || typeof command.default === 'function' ||
const valid = isConstructor(command, Command) || isConstructor(command.default, Command) ||
command instanceof Command || command.default instanceof Command;
if(ignoreInvalid && !valid) {
this.client.emit('warn', `Attempting to register an invalid command object: ${command}; skipping.`);
Expand Down Expand Up @@ -208,8 +208,8 @@ class CommandoRegistry {
*/
registerType(type) {
/* eslint-disable new-cap */
if(typeof type === 'function') type = new type(this.client);
else if(typeof type.default === 'function') type = new type.default(this.client);
if(isConstructor(type, ArgumentType)) type = new type(this.client);
else if(isConstructor(type.default, ArgumentType)) type = new type.default(this.client);
/* eslint-enable new-cap */

if(!(type instanceof ArgumentType)) throw new Error(`Invalid type object to register: ${type}`);
Expand Down Expand Up @@ -241,7 +241,7 @@ class CommandoRegistry {
registerTypes(types, ignoreInvalid = false) {
if(!Array.isArray(types)) throw new TypeError('Types must be an Array.');
for(const type of types) {
const valid = typeof type === 'function' || typeof type.default === 'function' ||
const valid = isConstructor(type, ArgumentType) || isConstructor(type.default, ArgumentType) ||
type instanceof ArgumentType || type.default instanceof ArgumentType;
if(ignoreInvalid && !valid) {
this.client.emit('warn', `Attempting to register an invalid argument type object: ${type}; skipping.`);
Expand Down Expand Up @@ -384,8 +384,8 @@ class CommandoRegistry {
*/
reregisterCommand(command, oldCommand) {
/* eslint-disable new-cap */
if(typeof command === 'function') command = new command(this.client);
else if(typeof command.default === 'function') command = new command.default(this.client);
if(isConstructor(command, Command)) command = new command(this.client);
else if(isConstructor(command.default, Command)) command = new command.default(this.client);
/* eslint-enable new-cap */

if(command.name !== oldCommand.name) throw new Error('Command name cannot change.');
Expand Down
18 changes: 17 additions & 1 deletion src/util.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// This returns Object.prototype in order to return a valid object
// without creating a new one each time this is called just to discard it the moment after.
const isConstructorProxyHandler = { construct() { return Object.prototype; } };

function escapeRegex(str) {
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
}
Expand All @@ -7,6 +11,17 @@ function disambiguation(items, label, property = 'name') {
return `Multiple ${label} found, please be more specific: ${itemList}`;
}

function isConstructor(func, _class) {
try {
// eslint-disable-next-line no-new
new new Proxy(func, isConstructorProxyHandler)();
if(!_class) return true;
return func.prototype instanceof _class;
} catch(err) {
return false;
}
}

function paginate(items, page = 1, pageLength = 10) {
const maxPage = Math.ceil(items.length / pageLength);
if(page < 1) page = 1;
Expand Down Expand Up @@ -55,5 +70,6 @@ module.exports = {
escapeRegex,
disambiguation,
paginate,
permissions
permissions,
isConstructor
};

0 comments on commit 59a0d8e

Please sign in to comment.