Utilities
Logger, sleep, debounce, throttle, and other helper utilities available to Plexcord plugins.
Utilities
Plexcord ships several utility modules that make plugin development easier. This page covers the most commonly-used helpers.
Logger
The Logger class gives your plugin a consistent, labeled console output, much easier to spot in DevTools than plain console.log.
Import
import { Logger } from "@utils/Logger";Creating a Logger
const logger = new Logger("MyPlugin");The first argument becomes the colored prefix in the console output: [MyPlugin].
Methods
| Method | Description | Console Method |
|---|---|---|
logger.log(...args) | General info | console.log |
logger.info(...args) | Info message | console.info |
logger.warn(...args) | Warning | console.warn |
logger.error(...args) | Error | console.error |
logger.debug(...args) | Debug info | console.debug |
const logger = new Logger("MyPlugin");
logger.log("Plugin started with settings:", settings.store);
logger.info("Processing message from", message.author.username);
logger.warn("Deprecated API used, consider updating");
logger.error("Failed to fetch data:", error);
logger.debug("Patch result:", patchedFunction.toString().slice(0, 200));Optional Color
// Second argument: CSS color for the label
const logger = new Logger("MyPlugin", "#43B581");sleep
Pauses execution for a given number of milliseconds. Works with async/await.
Import
import { sleep } from "@utils/misc";Usage
async function retryWithDelay() {
for (let i = 0; i < 3; i++) {
try {
await fetchData();
break;
} catch (e) {
logger.warn(`Attempt ${i + 1} failed, retrying in 1s...`);
await sleep(1000);
}
}
}debounce
Wraps a function so it only executes after a specified delay has passed since the last call. Useful for search inputs or expensive event handlers.
Import
import { debounce } from "@utils/debounce";Signature
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): TUsage
import { debounce } from "@utils/debounce";
// Without debounce: fires on every keystroke
function onSearchChange(value: string) {
expensiveSearch(value);
}
// With debounce: fires 300ms after the user stops typing
const onSearchChange = debounce((value: string) => {
expensiveSearch(value);
}, 300);In a Flux handler
const processUpdate = debounce((data) => {
updateUI(data);
}, 100);
flux: {
TYPING_START(event) {
processUpdate(event); // Won't spam updateUI
}
}makeLazy
Creates a value that is computed only once, the first time it's accessed.
Import
import { makeLazy } from "@utils/lazy";Usage
import { makeLazy } from "@utils/lazy";
// Computed only once, on first access
const expensiveValue = makeLazy(() => {
return someExpensiveComputation();
});
// Acts like a function: call it to get the value
const result = expensiveValue();proxyLazy and LazyComponent
These are used to defer module lookups until runtime, even when they are assigned at the module's top level.
import { LazyComponent } from "@utils/lazyReact";
import { proxyLazy } from "@utils/lazy";
// Deferred React component import
const MyComponent = LazyComponent(() => findByCode("someUniqueCode"));
// Deferred module import
const DiscordModule = proxyLazy(() => findByProps("someDiscordProp"));Most @webpack/common exports are already wrapped in proxyLazy. Use LazyComponent when you need to import a React component from Discord's Webpack.
classes
Merges CSS class names conditionally, similar to the popular classnames package.
Import
import { classes } from "@utils/misc";Usage
import { classes } from "@utils/misc";
const className = classes(
"base-class",
isActive && "active",
isDisabled && "disabled",
customClass
);
// e.g., "base-class active" or "base-class disabled customClass"Queue
An async queue that runs tasks one at a time, preventing race conditions.
Import
import { Queue } from "@utils/Queue";Usage
import { Queue } from "@utils/Queue";
const queue = new Queue();
// Tasks are executed sequentially even if added concurrently
users.forEach(user => {
queue.push(async () => {
await processUser(user);
});
});Common Pattern: Plugin with Utilities
import definePlugin from "@utils/types";
import { Logger } from "@utils/Logger";
import { debounce } from "@utils/debounce";
import { sleep } from "@utils/misc";
const logger = new Logger("RateLimitHelper", "#FAA61A");
export default definePlugin({
name: "RateLimitHelper",
description: "Demonstrates common utilities",
authors: [{ name: "You", id: 0n }],
start() {
logger.info("Plugin started");
this.setupHandlers();
},
stop() {
logger.info("Plugin stopped");
},
setupHandlers() {
this.handleEvent = debounce(async (data) => {
logger.debug("Processing event:", data);
await sleep(50); // Tiny pause to avoid rate limits
processData(data);
}, 200);
}
});