How do I get this code to work for sub folders (folders inside the main 'commands' folder)?

Viewed 499

How do I get this code to work for sub folders (folders inside the main 'commands' folder)? Here is some of my code.

Index.js:

const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));

for (const file of commandFiles) {
    const command = require(`./commands/${file}`);
    client.commands.set(command.name, command);
}
client.on('message', message => {
    if (!message.content.startsWith(prefix) || message.author.bot) return;

    const args = message.content.slice(prefix.length).trim().split(/ +/);
    const command = args.shift().toLowerCase();

    if (!client.commands.has(command)) return;

    try {
        client.commands.get(command).execute(message, args, client);
    } catch (error) {
        console.error(error);
        const errembed = new Discord.MessageEmbed()
        .setColor('#009ACD')
        .setDescription('There was an error trying to execute that command!')
        message.channel.send(errembed);
    }   
});

Examplecommand.js:

const Discord = require('discord.js')
module.exports = {
      name: 'command name',
      description: 'command description',
      execute(message, args) {
//code here
      }
}
3 Answers

I don't know a way to get this to work in sub-folders with an elegant solution, so I think this is what you should do:

client.subFolderCommands = new Discord.Collection();

const subFolderFiles = fs.readdirSync('./commands/subfolder').filter(file => file.endsWith('.js'));

for (var i = 0; i < subFolderFiles.length; i++) {
    const command = require(`./commands/subfolder/${subFolderFiles[i]}`);
    client.subFolderCommands.set(command.name, command);
}

If you know a better solution, please tell me!

What you can do, is make the readdirSync method return the files and/or directories as directory entry (Dirent). You can do this by setting the option withFileTypes to true in the fs.readdirSync method. Then you can iterate over the results and if an entry is a subdirectory, read all the files/directories from that folder. You can check this with the isDirectory() function.

Here's an example that could work:

function readFilesFromPath(pathString) {
    const directoryEntries = fs.readdirSync(pathString, {withFileTypes: true});

    return directoryEntries.reduce((filteredEntries, dirEnt) => {
        if (dirEnt.isDirectory()) {
            // If the entry is a directory, call this function again
            // but now add the directory name to the path string.
            filteredEntries.push(...readFilesFromPath(`${pathString}/${dirEnt.name}`))
        } else if (dirEnt.isFile()) {
            // Check if the entry is a file instead. And if so, check
            // if the file name ends with `.js`.
            if (dirEnt.name.endsWith('.js') {
                // Add the file to the command file array.
                filteredEntries.push(`${pathString}/${dirEnt.name}`);
            }
        }

        return filteredEntries;
    }, []);
}

// Call the read files function with the root folder of the commands and
// store all the file paths in the constant.
const commandFilePaths = readFilesFromPath('./commands');

// Loop over the array of file paths and set the command on the client.
commandFilePaths.forEach((filePath) => {
    const command = require(filePath);

    client.commands.set(command.name, command);
});

This is my command handler with subfolder support. Just replace first 5 lines with these

const commandFolders = fs.readdirSync('./commands');
for (const folder of commandFolders) {
    const commandFiles = fs.readdirSync(`./commands/${folder}`).filter(file => file.endsWith('.js'));
    for (const file of commandFiles) {
        const command = require(`./commands/${folder}/${file}`);
        client.commands.set(command.name, command);
    }
}
Related