Add utility functions
This commit is contained in:
parent
51a4a0eb09
commit
73116fbd34
|
@ -0,0 +1,2 @@
|
|||
type ObjectLiteral = {[index: string|number]: unknown};
|
||||
type AnyFunction = (...args: unknown[]) => unknown;
|
|
@ -0,0 +1,19 @@
|
|||
export function getKeys(object: ObjectLiteral) {
|
||||
const keys: Array<keyof typeof object> = [];
|
||||
|
||||
for (const key in object) keys.push(key);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
export default function cloneObject(target: ObjectLiteral, newObject: ObjectLiteral = {}, keys?: Array<keyof ObjectLiteral>) {
|
||||
if (!Array.isArray(keys)) keys = getKeys(target);
|
||||
|
||||
return keys.reduce((clone, key) => {
|
||||
if (typeof(target[key]) === "object" && !Array.isArray(target[key]) && target[key] !== null) clone[key] = cloneObject(target[key] as ObjectLiteral, {});
|
||||
else if (typeof target[key] === "function") clone[key] = (<AnyFunction>target[key]).bind(target);
|
||||
else clone[key] = target[key];
|
||||
|
||||
return clone;
|
||||
}, newObject);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Returns a function, that, as long as it continues to be invoked, will not
|
||||
* be triggered. The function will be called after it stops being called for
|
||||
* N milliseconds.
|
||||
*
|
||||
*
|
||||
* @param {function} executor
|
||||
* @param {number} delay
|
||||
*/
|
||||
export default function debounce(executor: AnyFunction, delay: number) {
|
||||
let timeout: NodeJS.Timeout;
|
||||
return function(...args: unknown[]) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => executor(...args), delay);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
/**
|
||||
* Deep extends an object with a set of other objects. Objects later in the list
|
||||
* of `extenders` have priority, that is to say if one sets a key to be a primitive,
|
||||
* it will be overwritten with the next one with the same key. If it is an object,
|
||||
* and the keys match, the object is extended. This happens recursively.
|
||||
* @param {object} extendee - Object to be extended
|
||||
* @param {...object} extenders - Objects to extend with
|
||||
* @returns {object} - A reference to `extendee`
|
||||
*/
|
||||
export default function extend(extendee: ObjectLiteral, ...extenders: ObjectLiteral[]) {
|
||||
for (let i = 0; i < extenders.length; i++) {
|
||||
for (const key in extenders[i]) {
|
||||
if (extenders[i].hasOwnProperty(key)) {
|
||||
if (typeof extendee[key] === "object" && typeof extenders[i][key] === "object") {
|
||||
extend(extendee[key] as ObjectLiteral, extenders[i][key] as ObjectLiteral);
|
||||
}
|
||||
else if (typeof extenders[i][key] === "object") {
|
||||
extendee[key] = {};
|
||||
extend(extendee[key] as ObjectLiteral, extenders[i][key] as ObjectLiteral);
|
||||
}
|
||||
else {
|
||||
extendee[key] = extenders[i][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return extendee;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
type TreeFilter = (o: unknown) => boolean
|
||||
|
||||
/**
|
||||
* Finds a value, subobject, or array from a tree that matches a specific filter.
|
||||
* @param {object} tree Tree that should be walked
|
||||
* @param {callable} searchFilter Filter to check against each object and subobject
|
||||
* @param {object} options Additional options to customize the search
|
||||
* @param {Array<string>|null} [options.walkable=null] Array of strings to use as keys that are allowed to be walked on. Null value indicates all keys are walkable
|
||||
* @param {Array<string>} [options.ignore=[]] Array of strings to use as keys to exclude from the search, most helpful when `walkable = null`.
|
||||
*/
|
||||
export default function findInTree(tree: ObjectLiteral | null, searchFilter: TreeFilter | string, {walkable = null, ignore = []}: {walkable?: string[] | null, ignore?: string[]} = {}): unknown {
|
||||
if (typeof searchFilter === "string") {
|
||||
if (tree?.hasOwnProperty(searchFilter)) return tree[searchFilter];
|
||||
}
|
||||
else if (searchFilter(tree)) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (typeof tree !== "object" || tree == null) return undefined;
|
||||
|
||||
let tempReturn: unknown;
|
||||
if (tree instanceof Array) {
|
||||
for (const value of tree) {
|
||||
tempReturn = findInTree(value, searchFilter, {walkable, ignore});
|
||||
if (typeof tempReturn != "undefined") return tempReturn;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const toWalk = walkable == null ? Object.keys(tree) : walkable;
|
||||
for (const key of toWalk) {
|
||||
if (typeof(tree[key]) == "undefined" || ignore.includes(key)) continue;
|
||||
tempReturn = findInTree(tree[key] as ObjectLiteral, searchFilter, {walkable, ignore});
|
||||
if (typeof tempReturn != "undefined") return tempReturn;
|
||||
}
|
||||
}
|
||||
return tempReturn;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Format strings with placeholders (`{{placeholder}}`) into full strings.
|
||||
* Quick example: `PluginUtilities.formatString("Hello, {{user}}", {user: "Zerebos"})`
|
||||
* would return "Hello, Zerebos".
|
||||
* @param {string} string - string to format
|
||||
* @param {object} values - object literal of placeholders to replacements
|
||||
* @returns {string} the properly formatted string
|
||||
*/
|
||||
export default function formatString(string: string, values: {[index: string]: string | object}) {
|
||||
for (const val in values) {
|
||||
let replacement = values[val];
|
||||
if (Array.isArray(replacement)) replacement = JSON.stringify(replacement);
|
||||
if (typeof(replacement) === "object" && replacement !== null) replacement = replacement.toString();
|
||||
string = string.replace(new RegExp(`{{${val}}}`, "g"), replacement);
|
||||
}
|
||||
return string;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
/**
|
||||
* List of logging types.
|
||||
*/
|
||||
enum LogTypes {
|
||||
ERROR = "error",
|
||||
DEBUG = "debug",
|
||||
LOG = "log",
|
||||
WARN = "warn",
|
||||
INFO = "info"
|
||||
};
|
||||
|
||||
export default class Logger {
|
||||
|
||||
/**
|
||||
* Logs an error using a collapsed error group with stacktrace.
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {string} message - Message or error to have logged.
|
||||
* @param {Error} error - Error object to log with the message.
|
||||
*/
|
||||
static stacktrace(module: string, message: string, error: Error) {
|
||||
console.error(`%c[${module}]%c ${message}\n\n%c`, "color: #3a71c1; font-weight: 700;", "color: red; font-weight: 700;", "color: red;", error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs using error formatting. For logging an actual error object consider {@link module:Logger.stacktrace}
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {string} message - Messages to have logged.
|
||||
*/
|
||||
static err(module: string, ...message: string[]) {Logger._log(module, message, LogTypes.ERROR);}
|
||||
|
||||
/**
|
||||
* Alias for "err"
|
||||
* @param {string} module NAme of the calling module
|
||||
* @param {...any} message Messages to have logged.
|
||||
*/
|
||||
static error(module: string, ...message: string[]) {Logger._log(module, message, LogTypes.ERROR);}
|
||||
|
||||
/**
|
||||
* Logs a warning message.
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {...any} message - Messages to have logged.
|
||||
*/
|
||||
static warn(module: string, ...message: string[]) {Logger._log(module, message, LogTypes.WARN);}
|
||||
|
||||
/**
|
||||
* Logs an informational message.
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {...any} message - Messages to have logged.
|
||||
*/
|
||||
static info(module: string, ...message: string[]) {Logger._log(module, message, LogTypes.INFO);}
|
||||
|
||||
/**
|
||||
* Logs used for debugging purposes.
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {...any} message - Messages to have logged.
|
||||
*/
|
||||
static debug(module: string, ...message: string[]) {Logger._log(module, message, LogTypes.DEBUG);}
|
||||
|
||||
/**
|
||||
* Logs used for basic loggin.
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {...any} message - Messages to have logged.
|
||||
*/
|
||||
static log(module: string, ...message: string[]) {Logger._log(module, message);}
|
||||
|
||||
/**
|
||||
* Logs strings using different console levels and a module label.
|
||||
*
|
||||
* @param {string} module - Name of the calling module.
|
||||
* @param {any|Array<any>} message - Messages to have logged.
|
||||
* @param {module:Logger.LogTypes} type - Type of log to use in console.
|
||||
*/
|
||||
static _log(module: string, message: string|string[], type: LogTypes = LogTypes.LOG) {
|
||||
if (!Array.isArray(message)) message = [message];
|
||||
console[type](`%c[BetterDiscord]%c [${module}]%c`, "color: #3E82E5; font-weight: 700;", "color: #3a71c1;", "", ...message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Generates an automatically memoizing version of an object.
|
||||
* @param {Object} object - object to memoize
|
||||
* @returns {Proxy} the proxy to the object that memoizes properties
|
||||
*/
|
||||
export default function memoizeObject(object: ObjectLiteral) {
|
||||
const proxy = new Proxy(object, {
|
||||
get: function(obj, mod) {
|
||||
if (typeof(mod) === "symbol") return null;
|
||||
if (!obj.hasOwnProperty(mod)) return undefined;
|
||||
if (Object.getOwnPropertyDescriptor(obj, mod)?.get) {
|
||||
const value = obj[mod];
|
||||
delete obj[mod];
|
||||
obj[mod] = value;
|
||||
}
|
||||
return obj[mod];
|
||||
},
|
||||
set: function(obj, mod, value) {
|
||||
if (typeof(mod) === "symbol") return false;
|
||||
if (obj.hasOwnProperty(mod)) return false;
|
||||
obj[mod] = value;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(proxy, "hasOwnProperty", {value: function(prop: string) {
|
||||
return this[prop] !== undefined;
|
||||
}});
|
||||
|
||||
return proxy;
|
||||
}
|
|
@ -41,5 +41,5 @@
|
|||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": ["./src/types.ts"]
|
||||
// "include": ["./src/types.ts"]
|
||||
}
|
Loading…
Reference in New Issue