UNPKG

dscaffold

Version:

A TypeScript framework for scaffolding modular Discord bot projects with dynamic command and event loading

238 lines 10.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.dscaffold = exports.Dscaffold = void 0; const discord_js_1 = require("discord.js"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); class Dscaffold { constructor() { this.commands = new discord_js_1.Collection(); this.categories = new discord_js_1.Collection(); } /** * Dynamically load commands from a directory * @param client - Discord.js Client instance * @param commandsPath - Path to commands directory (relative to project root) * @param options - Loading options */ async loadCommands(client, commandsPath, options = {}) { const { recursive = true, fileExtensions = ['.js', '.ts'], excludeDirs = ['node_modules', '.git', 'dist'] } = options; const resolvedPath = path_1.default.resolve(commandsPath); if (!fs_1.default.existsSync(resolvedPath)) { throw new Error(`Commands directory not found: ${resolvedPath}`); } await this.loadCommandsRecursive(client, resolvedPath, recursive, fileExtensions, excludeDirs); console.log(`✅ Loaded ${this.commands.size} commands from ${commandsPath}`); // Log categories this.categories.forEach((commands, category) => { console.log(` 📁 ${category}: ${commands.length} commands`); }); } /** * Dynamically load events from a directory * @param client - Discord.js Client instance * @param eventsPath - Path to events directory (relative to project root) * @param options - Loading options */ async loadEvents(client, eventsPath, options = {}) { const { recursive = true, fileExtensions = ['.js', '.ts'], excludeDirs = ['node_modules', '.git', 'dist'] } = options; const resolvedPath = path_1.default.resolve(eventsPath); if (!fs_1.default.existsSync(resolvedPath)) { throw new Error(`Events directory not found: ${resolvedPath}`); } let eventCount = 0; await this.loadEventsRecursive(client, resolvedPath, recursive, fileExtensions, excludeDirs, (count) => { eventCount = count; }); console.log(`✅ Loaded ${eventCount} events from ${eventsPath}`); } /** * Get command by name */ getCommand(name) { return this.commands.get(name); } /** * Get commands by category */ getCommandsByCategory(category) { return this.categories.get(category) || []; } /** * Get all categories */ getCategories() { return Array.from(this.categories.keys()); } async loadCommandsRecursive(client, dirPath, recursive, fileExtensions, excludeDirs) { const items = fs_1.default.readdirSync(dirPath); for (const item of items) { const itemPath = path_1.default.join(dirPath, item); const stat = fs_1.default.statSync(itemPath); if (stat.isDirectory()) { if (recursive && !excludeDirs.includes(item)) { await this.loadCommandsRecursive(client, itemPath, recursive, fileExtensions, excludeDirs); } } else if (stat.isFile()) { const ext = path_1.default.extname(item); if (fileExtensions.includes(ext)) { try { // Clear require cache for hot reloading in development delete require.cache[require.resolve(itemPath)]; const commandModule = require(itemPath); // Handle both module.exports and export default const command = commandModule.default || commandModule; if (this.isValidCommand(command)) { this.commands.set(command.name, command); // Organize by category const category = command.category || 'general'; if (!this.categories.has(category)) { this.categories.set(category, []); } this.categories.get(category).push(command); console.log(` 📄 Loaded command: ${command.name} (${category})`); } else { console.warn(` ⚠️ Invalid command file: ${itemPath}`); } } catch (error) { console.error(` ❌ Error loading command from ${itemPath}:`, error); } } } } } async loadEventsRecursive(client, dirPath, recursive, fileExtensions, excludeDirs, onEventLoad) { const items = fs_1.default.readdirSync(dirPath); let eventCount = 0; for (const item of items) { const itemPath = path_1.default.join(dirPath, item); const stat = fs_1.default.statSync(itemPath); if (stat.isDirectory()) { if (recursive && !excludeDirs.includes(item)) { await this.loadEventsRecursive(client, itemPath, recursive, fileExtensions, excludeDirs, onEventLoad); } } else if (stat.isFile()) { const ext = path_1.default.extname(item); if (fileExtensions.includes(ext)) { try { // Clear require cache for hot reloading in development delete require.cache[require.resolve(itemPath)]; const eventModule = require(itemPath); // Handle both module.exports and export default const event = eventModule.default || eventModule; if (this.isValidEvent(event)) { if (event.once) { client.once(event.name, (...args) => event.execute(...args)); } else { client.on(event.name, (...args) => event.execute(...args)); } eventCount++; console.log(` 📄 Loaded event: ${event.name} (${event.once ? 'once' : 'on'})`); } else { console.warn(` ⚠️ Invalid event file: ${itemPath}`); } } catch (error) { console.error(` ❌ Error loading event from ${itemPath}:`, error); } } } } onEventLoad(eventCount); } isValidCommand(command) { return (command && typeof command === 'object' && typeof command.name === 'string' && typeof command.execute === 'function'); } isValidEvent(event) { return (event && typeof event === 'object' && typeof event.name === 'string' && typeof event.execute === 'function'); } /** * Reload commands (useful for development) */ async reloadCommands(client, commandsPath) { this.commands.clear(); this.categories.clear(); await this.loadCommands(client, commandsPath); } /** * Reload a specific command */ async reloadCommand(commandName, commandsPath) { // Find the command file const commandFile = this.findCommandFile(commandName, commandsPath); if (!commandFile) { return false; } try { // Clear require cache delete require.cache[require.resolve(commandFile)]; const commandModule = require(commandFile); const command = commandModule.default || commandModule; if (this.isValidCommand(command)) { this.commands.set(command.name, command); console.log(`✅ Reloaded command: ${command.name}`); return true; } } catch (error) { console.error(`❌ Error reloading command ${commandName}:`, error); } return false; } findCommandFile(commandName, basePath) { const searchPaths = [ path_1.default.join(basePath, `${commandName}.js`), path_1.default.join(basePath, `${commandName}.ts`), path_1.default.join(basePath, `${commandName}/index.js`), path_1.default.join(basePath, `${commandName}/index.ts`), ]; for (const searchPath of searchPaths) { if (fs_1.default.existsSync(searchPath)) { return searchPath; } } // Recursive search return this.findCommandFileRecursive(commandName, basePath); } findCommandFileRecursive(commandName, dirPath) { if (!fs_1.default.existsSync(dirPath)) return null; const items = fs_1.default.readdirSync(dirPath); for (const item of items) { const itemPath = path_1.default.join(dirPath, item); const stat = fs_1.default.statSync(itemPath); if (stat.isDirectory()) { const result = this.findCommandFileRecursive(commandName, itemPath); if (result) return result; } else if (stat.isFile()) { const name = path_1.default.basename(item, path_1.default.extname(item)); if (name === commandName) { return itemPath; } } } return null; } } exports.Dscaffold = Dscaffold; // Create a singleton instance exports.dscaffold = new Dscaffold(); exports.default = exports.dscaffold; //# sourceMappingURL=dscaffold.js.map