Plexcord LogoPlexcord
API Reference

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

MethodDescriptionConsole Method
logger.log(...args)General infoconsole.log
logger.info(...args)Info messageconsole.info
logger.warn(...args)Warningconsole.warn
logger.error(...args)Errorconsole.error
logger.debug(...args)Debug infoconsole.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
): T

Usage

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);
    }
});

Next Steps

On this page