diff --git a/bin/cli.js b/bin/cli.js index b91192d..9e5eeba 100644 --- a/bin/cli.js +++ b/bin/cli.js @@ -5,7 +5,7 @@ const tColor=(e,t)=>e.replace("%s",t);const __dirname=path.dirname(fileURLToPath // Ensure the package.json exists try{const e=path.resolve(__dirname,"..","package.json");currentPackage=JSON.parse(fs.readFileSync(e,"utf-8"))}catch{ // No need, since user could run discraft init directly from npx, so this err log would be baseless -}const showBranding=(e=tFmt.cyan,t="Discraft-js")=>{try{console.log(e,figlet.textSync(t,{font:"Small",horizontalLayout:"default",verticalLayout:"default",width:80,whitespaceBreak:true},"\n"))}catch{console.error("Failed to show branding")}};const program=new Command;program.version(currentPackage?.version||"1.0.0").name("discraft").description("Discraft CLI - Best framework for Discord bots");program.command("init").argument("[project name]","project name").argument("[project directory]","project directory").addOption(new Option("-li, --license ","project license").choices(availableLicenses)).addOption(new Option("-ni, --no-install","skip deps installation")).addOption(new Option("-af, --all-features","allow all features")).description("Initialize a new Discraft project").action((async(e,t,n)=>{showBranding();try{ +}const showBranding=(e=tFmt.cyan,t="Discraft")=>{try{console.log(e,figlet.textSync(t,{font:"Small",horizontalLayout:"default",verticalLayout:"default",width:80,whitespaceBreak:true},"\n"))}catch{console.error("Failed to show branding")}};const program=new Command;program.version(currentPackage?.version||"1.0.0").name("discraft").description("Discraft CLI - Best framework for Discord bots");program.command("init").argument("[project name]","project name").argument("[project directory]","project directory").addOption(new Option("-li, --license ","project license").choices(availableLicenses)).addOption(new Option("-i, --install","install dependencies")).addOption(new Option("-af, --all-features","allow all features")).description("Initialize a new Discraft project").action((async(e,t,n)=>{showBranding();try{ // Get project details const o={name:e,directory:t,additionalFeatures:["exampleCommands","envSetup","readme"],license:n.license};if(!o["name"]||!/^[a-zA-Z0-9-_]+$/.test(o.name)){o["name"]=await input({message:`Project name:`,default:path.basename(process.cwd()),validate:e=>{if(/^[a-zA-Z0-9-_]+$/.test(e))return true;return"Project name may only include letters, numbers, dashes and underscores"}})}if(!o.directory){const e=await input({message:`Project directory:`,default:o.name});o["directory"]=path.join(process.cwd(),e)}if(!availableLicenses.includes(o.license)){o["license"]=await select({message:"Project License:",choices:availableLicenses})} // Set up project directory @@ -29,14 +29,16 @@ if(o["additionalFeatures"].includes("readme")){const e=`# ${o.name}\nBot made wi // Create .gitignore const d=`.env\nnode_modules/\ndist/\n`;fs.writeFileSync(path.join(s,".gitignore"),d); // Welcome -const l=()=>{console.log(tFmt.green,tFmt.bold+"\n✨ Discraft project initialized successfully!");console.log(tFmt.grey,"\nNext steps:");if(o.directory!==process.cwd()){console.log(`\tRun ${tColor(tFmt.cyan,`cd ${o.directory.replace(process.cwd()+"/","")}`)} to enter your project directory.`)}console.log(`\tAdd your bot token and client ID to ${tColor(tFmt.cyan,".env")} file`);if(n.install==false)console.log(`\tRun ${tColor(tFmt.cyan,"npm install discord.js@latest dotenv@latest discraft@latest")} to install dependencies`);console.log(`\tRun ${tColor(tFmt.cyan,"npm run dev")} to start development\n`)}; +const l=()=>{console.log(tFmt.green,tFmt.bold+"\n✨ Discraft project initialized successfully!");console.log(tFmt.grey,"\nNext steps:");if(o.directory!==process.cwd()){console.log(`\tRun ${tColor(tFmt.cyan,`cd ${o.directory.replace(process.cwd()+"/","")}`)} to enter your project directory.`)}console.log(`\tAdd your bot token and client ID to ${tColor(tFmt.cyan,".env")} file`);if(n.install===false)console.log(`\tRun ${tColor(tFmt.cyan,"npm install discord.js@latest dotenv@latest discraft@latest")} to install dependencies`);console.log(`\tRun ${tColor(tFmt.cyan,"npm run dev")} to start development\n`)}; // Install latest dependencies -if(n.install!==false){console.log(tColor(tFmt.blue,"\n📦 Installing dependencies..."));const e=spawn("npm",["install","discord.js@latest","dotenv@latest","discraft@latest"],{stdio:"inherit",cwd:s});e.on("close",(e=>{if(e===0){l()}else{console.error(tFmt.red,'\n❌ Failed to install dependencies. Please run "npm install" manually.')}}))}else{l()}}catch(e){if(e.isTtyError){console.error(tFmt.red,"Prompt couldn't be rendered in the current environment")}else if(e.message==="Aborted"){console.log(tFmt.red,"\nProject initialization cancelled.")}else{console.error(tFmt.red,"Error during initialization:\n",e)}process.exit(1)}}));let activeProcess=null;process.on("SIGINT",(()=>{console.log("\nGracefully shutting down...");if(activeProcess){activeProcess.kill("SIGINT")}process.exit(0)}));program.command("dev").description("Start development server").action((()=>{showBranding(tFmt.blue);const e=path.join(__dirname,"..","scripts","dev.js");activeProcess=spawn("node",[e],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Dev process exited with code ${e}`)}}))}));program.command("build").description("Build for production").option("-y, --yes","Skip prompts and use defaults").option("-o, --output ","Output directory","dist").option("--max-optimize","Enable maximum optimization (slower build, faster runtime)",true).action((()=>{showBranding(tFmt.blue,"Building Discraft-js");console.log("\n");const e=path.join(__dirname,"..","scripts","build.js");activeProcess=spawn("node",[e,...process.argv.slice(3)],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname,BABEL_PRESET_ENV_PATH:path.join(__dirname,"..","node_modules","@babel/preset-env")}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Build process exited with code ${e}`)}}))}));program.command("start").description("Start production server").option("-d, --dir ","Build directory","dist").action((({dir:e})=>{showBranding(tFmt.blue,"Starting Discraft-js");console.log("\n");const t=path.join(__dirname,"..","scripts","start.js");activeProcess=spawn("node",[t,e],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Start process exited with code ${e}`)}}))}));program.command("test").description("Test your bot's configuration").addCommand(new Command("token").description("Check if the bot token is valid").action((()=>{const e=path.join(__dirname,"..","scripts","test-token.js");activeProcess=spawn("node",[e],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Token check process exited with code ${e}`)}}))})));program.command("add").on("command:*",invalidCmdHandler).description("Add new components to your bot").addCommand(new Command("command").description("Create a new Discord bot command").action((async()=>{showBranding(tFmt.blue);const e=path.join(__dirname,"..","scripts","add-command.js");try{const t=spawn("node",[e],{stdio:"inherit",shell:true});t.on("error",(e=>{console.error("Failed to start command generator:",e);process.exit(1)}));t.on("exit",(e=>{if(e!==0){console.error(`Command generator exited with code ${e}`);process.exit(e)}}))}catch(e){console.error("Error executing command generator:",e);process.exit(1)}}))).addCommand(new Command("event").description("Create a new Discord event handler").action((async()=>{showBranding(tFmt.blue);const e=path.join(__dirname,"..","scripts","add-event.js");try{const t=spawn("node",[e],{stdio:"inherit",shell:true});t.on("error",(e=>{console.error("Failed to start event generator:",e);process.exit(1)}));t.on("exit",(e=>{if(e!==0){console.error(`Event generator exited with code ${e}`);process.exit(e)}}))}catch(e){console.error("Error executing event generator:",e);process.exit(1)}})));program.on("command:*",invalidCmdHandler);program.addHelpText("beforeAll",tColor(tFmt.blue,figlet.textSync("Discraft-js",{font:"Standard",horizontalLayout:"default",verticalLayout:"default",width:80,whitespaceBreak:true},"\n")));program.addHelpText("before",tFmt.blue.split("%s")[1]);program.addHelpText("afterAll",`${tColor(tFmt.yellow,"\n⭐ Support Us by Starring Our Repo: https://github.com/The-Best-Codes/discraft-js")}`);program.parse(process.argv);function invalidCmdHandler(...e){showBranding(tFmt.red);console.log(tFmt.red,tFmt.bold+` Sorry, the command \`${e.join(" ").trim()}\` is not recognized. Please use a valid command.`); +if(n.install===true){console.log(tColor(tFmt.blue,"\n📦 Installing dependencies..."));const e=spawn("npm",["install","discord.js@latest","dotenv@latest","discraft@latest"],{stdio:"inherit",cwd:s});e.on("close",(e=>{if(e===0){l()}else{console.error(tFmt.red,'\n❌ Failed to install dependencies. Please run "npm install" manually.')}}))}else{l()}}catch(e){if(e.isTtyError){console.error(tFmt.red,"Prompt couldn't be rendered in the current environment")}else if(e.message==="Aborted"){console.log(tFmt.red,"\nProject initialization cancelled.")}else{console.error(tFmt.red,"Error during initialization:\n",e)}process.exit(1)}}));let activeProcess=null;process.on("SIGINT",(()=>{console.log("\nGracefully shutting down...");if(activeProcess){activeProcess.kill("SIGINT")}process.exit(0)}));program.command("dev").description("Start development server").action((()=>{showBranding(tFmt.blue);const e=path.join(__dirname,"..","scripts","dev.js");activeProcess=spawn("node",[e],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Dev process exited with code ${e}`)}}))}));program.command("build").description("Build for production").option("-y, --yes","Skip prompts and use defaults").option("-o, --output ","Output directory","dist").option("--max-optimize","Enable maximum optimization (slower build, faster runtime)",true).action((()=>{showBranding(tFmt.blue,"Building Discraft-js");console.log("\n");const e=path.join(__dirname,"..","scripts","build.js");activeProcess=spawn("node",[e,...process.argv.slice(3)],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname,BABEL_PRESET_ENV_PATH:path.join(__dirname,"..","node_modules","@babel/preset-env")}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Build process exited with code ${e}`)}}))}));program.command("start").description("Start production server").option("-d, --dir ","Build directory","dist").action((({dir:e})=>{showBranding(tFmt.blue,"Starting Discraft-js");console.log("\n");const t=path.join(__dirname,"..","scripts","start.js");activeProcess=spawn("node",[t,e],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Start process exited with code ${e}`)}}))}));program.command("test").description("Test your bot's configuration").addCommand(new Command("token").description("Check if the bot token is valid").action((()=>{const e=path.join(__dirname,"..","scripts","test-token.js");activeProcess=spawn("node",[e],{stdio:"inherit",cwd:process.cwd(),env:{...process.env,DISCRAFT_ROOT:__dirname}});activeProcess.on("close",(e=>{activeProcess=null;if(e!==0&&e!==null){console.error(`Token check process exited with code ${e}`)}}))})));program.command("add").on("command:*",invalidCmdHandler).description("Add new components to your bot").addCommand(new Command("command").description("Create a new Discord bot command").action((async()=>{showBranding(tFmt.blue);const e=path.join(__dirname,"..","scripts","add-command.js");try{const t=spawn("node",[e],{stdio:"inherit",shell:true});t.on("error",(e=>{console.error("Failed to start command generator:",e);process.exit(1)}));t.on("exit",(e=>{if(e!==0){console.error(`Command generator exited with code ${e}`);process.exit(e)}}))}catch(e){console.error("Error executing command generator:",e);process.exit(1)}}))).addCommand(new Command("event").description("Create a new Discord event handler").action((async()=>{showBranding(tFmt.blue);const e=path.join(__dirname,"..","scripts","add-event.js");try{const t=spawn("node",[e],{stdio:"inherit",shell:true});t.on("error",(e=>{console.error("Failed to start event generator:",e);process.exit(1)}));t.on("exit",(e=>{if(e!==0){console.error(`Event generator exited with code ${e}`);process.exit(e)}}))}catch(e){console.error("Error executing event generator:",e);process.exit(1)}})));program.on("command:*",invalidCmdHandler);program.addHelpText("beforeAll",tColor(tFmt.blue,figlet.textSync("Discraft-js",{font:"Standard",horizontalLayout:"default",verticalLayout:"default",width:80,whitespaceBreak:true},"\n")));program.addHelpText("before",tFmt.blue.split("%s")[1]);program.addHelpText("afterAll",`${tColor(tFmt.yellow,"\n⭐ Support Us by Starring Our Repo: https://github.com/The-Best-Codes/discraft-js")}`);program.parse(process.argv);function invalidCmdHandler(...e){showBranding(tFmt.red);console.log(tFmt.red,tFmt.bold+` Sorry, the command \`${e.join(" ").trim()}\` is not recognized. Please use a valid command.`); // Collect all commands including subcommands, formatted with parent-child hierarchy const t=[];program.commands.forEach((e=>{let n=e._name||"";let o=e.commands||[]; // Add the parent command itself t.push(n); // Add subcommands under the parent command o.forEach((e=>{t.push(`${n} ${e._name}`)}))})); +// Add the help command +t.push("help"); // Print available commands (parent commands and their subcommands) console.log(tFmt.red,` Available commands: ${t.join(", ")}`);process.exit(1)} \ No newline at end of file diff --git a/common/utils/logger.js b/common/utils/logger.js index 3b55928..fc38cb3 100644 --- a/common/utils/logger.js +++ b/common/utils/logger.js @@ -1 +1 @@ -import{logLevel}from"../../src/config/bot.config.js";const colors={reset:"",bright:"",dim:"",underscore:"",blink:"",reverse:"",hidden:"",fg:{black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:"",crimson:""},bg:{black:"",red:"",green:"",yellow:"",blue:"",magenta:"",cyan:"",white:"",crimson:""}};export const log=(o,...e)=>{console.log(`${colors.bg.white}[LOG]${colors.reset} ${o}`,...e)};export const info=(o,...e)=>{console.info(`${colors.fg.blue}[INFO]${colors.reset} ${o}`,...e)};export const warn=(o,...e)=>{console.warn(`${colors.bg.yellow}[WARN]${colors.reset} ${o}`,...e)};export const error=(o,...e)=>{console.error(`${colors.bg.red}[ERROR]${colors.reset} ${o}`,...e)};export const trace=(o,...e)=>{console.trace(`${colors.fg.crimson}[TRACE]${colors.reset} ${o}`,...e)};export const success=(o,...e)=>{console.log(`${colors.bg.green}[SUCCESS]${colors.reset} ${o}`,...e)};export const debug=(o,...e)=>{"debug"===logLevel&&console.debug(`${colors.fg.cyan}[DEBUG]${colors.reset} ${o}`,...e)}; \ No newline at end of file +import{logLevel}from"../../src/config/bot.config.js";import consola from"consola";export const log=consola.log;export const info=consola.info;export const warn=consola.warn;export const error=consola.error;export const trace=consola.trace;export const success=consola.success;export const debug=(o,...c)=>{"debug"===logLevel&&consola.debug(o,...c)}; \ No newline at end of file diff --git a/editing/bin/cli.js b/editing/bin/cli.js index 79b7963..3e7f0f4 100644 --- a/editing/bin/cli.js +++ b/editing/bin/cli.js @@ -35,7 +35,7 @@ try { // No need, since user could run discraft init directly from npx, so this err log would be baseless } -const showBranding = (color = tFmt.cyan, txt = "Discraft-js") => { +const showBranding = (color = tFmt.cyan, txt = "Discraft") => { try { console.log( color, @@ -48,8 +48,8 @@ const showBranding = (color = tFmt.cyan, txt = "Discraft-js") => { width: 80, whitespaceBreak: true, }, - "\n" - ) + "\n", + ), ); } catch { console.error("Failed to show branding"); @@ -69,10 +69,10 @@ program .argument("[project directory]", "project directory") .addOption( new Option("-li, --license ", "project license").choices( - availableLicenses - ) + availableLicenses, + ), ) - .addOption(new Option("-ni, --no-install", "skip deps installation")) + .addOption(new Option("-i, --install", "install dependencies")) .addOption(new Option("-af, --all-features", "allow all features")) .description("Initialize a new Discraft project") .action(async (projectName, projectDirectory, cmdOptions) => { @@ -154,7 +154,7 @@ program "..", "src", "config", - "bot.config.js" + "bot.config.js", ), "discraft/commands/handler.js": path.join( __dirname, @@ -162,7 +162,7 @@ program "src", "discraft", "commands", - "handler.js" + "handler.js", ), "discraft/events/handler.js": path.join( __dirname, @@ -170,42 +170,42 @@ program "src", "discraft", "events", - "handler.js" + "handler.js", ), "services/discord.js": path.join( __dirname, "..", "src", "services", - "discord.js" + "discord.js", ), "utils/logger.js": path.join( __dirname, "..", "src", "utils", - "logger.js" + "logger.js", ), "utils/commandCache.js": path.join( __dirname, "..", "src", "utils", - "commandCache.js" + "commandCache.js", ), "events/ready.js": path.join( __dirname, "..", "src", "events", - "ready.js" + "ready.js", ), "events/error.js": path.join( __dirname, "..", "src", "events", - "error.js" + "error.js", ), "index.js": path.join(__dirname, "..", "src", "index.js"), }; @@ -241,21 +241,21 @@ program "..", "src", "commands", - "ping.js" + "ping.js", ); templateFiles["commands/random.js"] = path.join( __dirname, "..", "src", "commands", - "random.js" + "random.js", ); templateFiles["commands/status.js"] = path.join( __dirname, "..", "src", "commands", - "status.js" + "status.js", ); } @@ -285,7 +285,7 @@ program fs.writeFileSync( path.join(projectDir, "package.json"), - JSON.stringify(pkg, null, 2) + JSON.stringify(pkg, null, 2), ); // Create .env and .env.example if selected @@ -328,17 +328,19 @@ Bot made with Discraft - \`discraft build\`: Build for production - \`discraft start\`: Start production server -${projectConfig.additionalFeatures.includes("exampleCommands") - ? "\n### Bot Commands:\n- `/ping`: Check bot latency\n\n- `/random [pick, number]`: Pick something random out of a list; or pick a random number between the min and max\n\n- `/status`: Check bot and server status" - : "" - } +${ + projectConfig.additionalFeatures.includes("exampleCommands") + ? "\n### Bot Commands:\n- `/ping`: Check bot latency\n\n- `/random [pick, number]`: Pick something random out of a list; or pick a random number between the min and max\n\n- `/status`: Check bot and server status" + : "" +} ## License -${projectConfig.license === "None" - ? "This project is not licensed." - : `This project is licensed under the ${projectConfig.license} License.` - } +${ + projectConfig.license === "None" + ? "This project is not licensed." + : `This project is licensed under the ${projectConfig.license} License.` +} `; fs.writeFileSync(path.join(projectDir, "README.md"), readme); } @@ -351,37 +353,37 @@ ${projectConfig.license === "None" const welcomeUser = () => { console.log( tFmt.green, - tFmt.bold + "\n✨ Discraft project initialized successfully!" + tFmt.bold + "\n✨ Discraft project initialized successfully!", ); console.log(tFmt.grey, "\nNext steps:"); if (projectConfig.directory !== process.cwd()) { console.log( `\tRun ${tColor( tFmt.cyan, - `cd ${projectConfig.directory.replace(process.cwd() + "/", "")}` - )} to enter your project directory.` + `cd ${projectConfig.directory.replace(process.cwd() + "/", "")}`, + )} to enter your project directory.`, ); } console.log( `\tAdd your bot token and client ID to ${tColor( tFmt.cyan, - ".env" - )} file` + ".env", + )} file`, ); - if (cmdOptions.install == false) + if (cmdOptions.install === false) console.log( `\tRun ${tColor( tFmt.cyan, - "npm install discord.js@latest dotenv@latest discraft@latest" - )} to install dependencies` + "npm install discord.js@latest dotenv@latest discraft@latest", + )} to install dependencies`, ); console.log( - `\tRun ${tColor(tFmt.cyan, "npm run dev")} to start development\n` + `\tRun ${tColor(tFmt.cyan, "npm run dev")} to start development\n`, ); }; // Install latest dependencies - if (cmdOptions.install !== false) { + if (cmdOptions.install === true) { console.log(tColor(tFmt.blue, "\n📦 Installing dependencies...")); const npmInstall = spawn( "npm", @@ -389,7 +391,7 @@ ${projectConfig.license === "None" { stdio: "inherit", cwd: projectDir, - } + }, ); npmInstall.on("close", (code) => { @@ -398,7 +400,7 @@ ${projectConfig.license === "None" } else { console.error( tFmt.red, - '\n❌ Failed to install dependencies. Please run "npm install" manually.' + '\n❌ Failed to install dependencies. Please run "npm install" manually.', ); } }); @@ -409,7 +411,7 @@ ${projectConfig.license === "None" if (err.isTtyError) { console.error( tFmt.red, - "Prompt couldn't be rendered in the current environment" + "Prompt couldn't be rendered in the current environment", ); } else if (err.message === "Aborted") { console.log(tFmt.red, "\nProject initialization cancelled."); @@ -457,7 +459,7 @@ program .option( "--max-optimize", "Enable maximum optimization (slower build, faster runtime)", - true + true, ) .action(() => { showBranding(tFmt.blue, "Building Discraft-js"); @@ -473,7 +475,7 @@ program __dirname, "..", "node_modules", - "@babel/preset-env" + "@babel/preset-env", ), }, }); @@ -517,7 +519,7 @@ program __dirname, "..", "scripts", - "test-token.js" + "test-token.js", ); activeProcess = spawn("node", [scriptPath], { stdio: "inherit", @@ -530,7 +532,7 @@ program console.error(`Token check process exited with code ${code}`); } }); - }) + }), ); program @@ -546,7 +548,7 @@ program __dirname, "..", "scripts", - "add-command.js" + "add-command.js", ); try { const child = spawn("node", [scriptPath], { @@ -569,7 +571,7 @@ program console.error("Error executing command generator:", err); process.exit(1); } - }) + }), ) .addCommand( new Command("event") @@ -580,7 +582,7 @@ program __dirname, "..", "scripts", - "add-event.js" + "add-event.js", ); try { const child = spawn("node", [scriptPath], { @@ -603,7 +605,7 @@ program console.error("Error executing event generator:", err); process.exit(1); } - }) + }), ); program.on("command:*", invalidCmdHandler); @@ -621,17 +623,17 @@ program.addHelpText( width: 80, whitespaceBreak: true, }, - "\n" - ) - ) + "\n", + ), + ), ); program.addHelpText("before", tFmt.blue.split("%s")[1]); program.addHelpText( "afterAll", `${tColor( tFmt.yellow, - "\n⭐ Support Us by Starring Our Repo: https://github.com/The-Best-Codes/discraft-js" - )}` + "\n⭐ Support Us by Starring Our Repo: https://github.com/The-Best-Codes/discraft-js", + )}`, ); program.parse(process.argv); @@ -642,9 +644,9 @@ function invalidCmdHandler(...cmd) { console.log( tFmt.red, tFmt.bold + - ` Sorry, the command \`${cmd - .join(" ") - .trim()}\` is not recognized. Please use a valid command.` + ` Sorry, the command \`${cmd + .join(" ") + .trim()}\` is not recognized. Please use a valid command.`, ); // Collect all commands including subcommands, formatted with parent-child hierarchy @@ -663,6 +665,9 @@ function invalidCmdHandler(...cmd) { }); }); + // Add the help command + allCommands.push("help"); + // Print available commands (parent commands and their subcommands) console.log(tFmt.red, ` Available commands: ${allCommands.join(", ")}`); diff --git a/editing/common/utils/logger.js b/editing/common/utils/logger.js index eaf48f7..c780847 100644 --- a/editing/common/utils/logger.js +++ b/editing/common/utils/logger.js @@ -1,63 +1,14 @@ import { logLevel } from "../../src/config/bot.config.js"; +import consola from "consola"; -const colors = { - reset: "\x1b[0m", - bright: "\x1b[1m", - dim: "\x1b[2m", - underscore: "\x1b[4m", - blink: "\x1b[5m", - reverse: "\x1b[7m", - hidden: "\x1b[8m", - - fg: { - black: "\x1b[30m", - red: "\x1b[31m", - green: "\x1b[32m", - yellow: "\x1b[33m", - blue: "\x1b[34m", - magenta: "\x1b[35m", - cyan: "\x1b[36m", - white: "\x1b[37m", - crimson: "\x1b[38m" - }, - bg: { - black: "\x1b[40m", - red: "\x1b[41m", - green: "\x1b[42m", - yellow: "\x1b[43m", - blue: "\x1b[44m", - magenta: "\x1b[45m", - cyan: "\x1b[46m", - white: "\x1b[47m", - crimson: "\x1b[48m" - } -}; - -export const log = (message, ...args) => { - console.log(`${colors.bg.white}[LOG]${colors.reset} ${message}`, ...args); -}; - -export const info = (message, ...args) => { - console.info(`${colors.fg.blue}[INFO]${colors.reset} ${message}`, ...args); -}; - -export const warn = (message, ...args) => { - console.warn(`${colors.bg.yellow}[WARN]${colors.reset} ${message}`, ...args); -}; - -export const error = (message, ...args) => { - console.error(`${colors.bg.red}[ERROR]${colors.reset} ${message}`, ...args); -}; - -export const trace = (message, ...args) => { - console.trace(`${colors.fg.crimson}[TRACE]${colors.reset} ${message}`, ...args); -}; - -export const success = (message, ...args) => { - console.log(`${colors.bg.green}[SUCCESS]${colors.reset} ${message}`, ...args); -}; +export const log = consola.log; +export const info = consola.info; +export const warn = consola.warn; +export const error = consola.error; +export const trace = consola.trace; +export const success = consola.success; export const debug = (message, ...args) => { - if (logLevel !== "debug") return; - console.debug(`${colors.fg.cyan}[DEBUG]${colors.reset} ${message}`, ...args); -}; \ No newline at end of file + if (logLevel !== "debug") return; + consola.debug(message, ...args); +}; diff --git a/editing/scripts/add-command.js b/editing/scripts/add-command.js index f41a6b3..94dd1df 100644 --- a/editing/scripts/add-command.js +++ b/editing/scripts/add-command.js @@ -11,12 +11,12 @@ async function generateCommand() { if (!doesSrcDirExist) { if (isSrcDir) { error( - 'You are in the "src/" directory. You should be in the root of your Discraft project.' + 'You are in the "src/" directory. You should be in the root of your Discraft project.', ); process.exit(1); } else { error( - 'The "src/" directory does not exist. Please run "discraft init" to initialize a project, or ensure you are in the root of your Discraft project.' + 'The "src/" directory does not exist. Please run "discraft init" to initialize a project, or ensure you are in the root of your Discraft project.', ); process.exit(1); } @@ -103,7 +103,7 @@ async function generateCommand() { if (addCmdOptions) { console.log( - "\nAdding options to your command... For each option, you'll need to specify:" + "\nAdding options to your command... For each option, you'll need to specify:", ); console.log("- The type of data it accepts (text, number, etc.)"); console.log('- The name of the option (e.g., "user" in /ban )'); @@ -173,7 +173,9 @@ async function generateCommand() { .filter( (option) => option.name.toLowerCase().includes(input.toLowerCase()) || - option.description.toLowerCase().includes(input.toLowerCase()) + option.description + .toLowerCase() + .includes(input.toLowerCase()), ) .map((option) => ({ name: `${option.name} - ${option.description}`, // Show both name and description @@ -395,7 +397,7 @@ async function generateCommand() { fs.writeFileSync(filePath, commandContent); success( - `Command ${commandConfig["name"]} created successfully at ${filePath}` + `Command ${commandConfig["name"]} created successfully at ${filePath}`, ); // Return the command details for potential further use diff --git a/editing/scripts/add-event.js b/editing/scripts/add-event.js index f73e307..41a2735 100644 --- a/editing/scripts/add-event.js +++ b/editing/scripts/add-event.js @@ -13,12 +13,12 @@ async function generateEvent() { if (!doesSrcDirExist) { if (isSrcDir) { error( - 'You are in the "src/" directory. You should be in the root of your Discraft project.' + 'You are in the "src/" directory. You should be in the root of your Discraft project.', ); process.exit(1); } else { error( - 'The "src/" directory does not exist. Please run "discraft init" to initialize a project, or ensure you are in the root of your Discraft project.' + 'The "src/" directory does not exist. Please run "discraft init" to initialize a project, or ensure you are in the root of your Discraft project.', ); process.exit(1); } @@ -55,7 +55,7 @@ async function generateEvent() { // Filter options based on input return options .filter((option) => - option.name.toLowerCase().includes(input.toLowerCase()) + option.name.toLowerCase().includes(input.toLowerCase()), ) .map((option) => ({ name: `${option.name}`, // Show both name and description @@ -95,12 +95,12 @@ import { Events } from "discord.js"; export default (client) => { client.on(Events.${eventConfig.type}, (${ - eventConfig.type === "messageCreate" ? "message" : "event" - }) => { + eventConfig.type === "messageCreate" ? "message" : "event" + }) => { debug("'${eventConfig.name}' event triggered"); try { // Add your event handling logic here - + } catch (err) { error("Error in '${eventConfig.name}' event handler:", err); } @@ -112,7 +112,7 @@ export default (client) => { fs.writeFileSync(eventPath, eventContent); success( `Created event handler at src/events/${eventConfig.name}.js\n` + - `Event will trigger on: ${eventConfig.type}` + `Event will trigger on: ${eventConfig.type}`, ); return { name: eventConfig.name, diff --git a/editing/scripts/build.js b/editing/scripts/build.js index 50023b8..33a29c3 100644 --- a/editing/scripts/build.js +++ b/editing/scripts/build.js @@ -1,6 +1,6 @@ import fs from "fs"; import path from "path"; -import { info, error, success, log } from "../common/utils/logger.js"; +import { info, error, success } from "../common/utils/logger.js"; import { checkbox, confirm } from "@inquirer/prompts"; import { rollup } from "rollup"; import { getFileSizes, displaySizeComparison } from "./utils/fileSizeUtil.js"; @@ -12,8 +12,6 @@ import commonjs from "@rollup/plugin-commonjs"; import json from "@rollup/plugin-json"; import replace from "@rollup/plugin-replace"; import babel from "@rollup/plugin-babel"; -import { exec } from "child_process"; - const projectDir = process.cwd(); const srcDir = path.join(projectDir, "src"); @@ -127,15 +125,17 @@ async function build(options) { exports: "auto", minifyInternalExports: true, }, - external: config.standalone ? [] : (id) => { - return ( - !id.startsWith(".") && - !id.startsWith("/") && - !id.startsWith("src/") && - !id.startsWith("../") && - !id.startsWith("./") - ); - }, + external: config.standalone + ? [] + : (id) => { + return ( + !id.startsWith(".") && + !id.startsWith("/") && + !id.startsWith("src/") && + !id.startsWith("../") && + !id.startsWith("./") + ); + }, plugins: [ replace({ preventAssignment: true, @@ -194,7 +194,7 @@ async function build(options) { }); await fs.promises.writeFile( path.join(outputDir, "package.json"), - JSON.stringify(packageJson, null, 2) + JSON.stringify(packageJson, null, 2), ); info("Generated package.json with dependencies"); } else { @@ -217,7 +217,7 @@ async function build(options) { info(`Output location: ${outputDir}`); success( - "Build completed successfully in " + (Date.now() - startTime) + "ms" + "Build completed successfully in " + (Date.now() - startTime) + "ms", ); } catch (err) { if (err.name === "ExitPromptError") { @@ -248,8 +248,10 @@ async function getBuildConfig(options) { } // Check if "minify" is selected - console.log("\n") - const minify = await confirm({ message: 'Do you want to minify the code?' }); + console.log("\n"); + const minify = await confirm({ + message: "Do you want to minify the code?", + }); // If minify is selected, ask about additional options let additionalOptions = []; @@ -281,19 +283,19 @@ async function getBuildConfig(options) { value: "standalone", name: "Create standalone bundle with all dependencies included", checked: false, - } - ] + }, + ], }); } // Return the final configuration return { minify: minify, - keepFunctionNames: additionalOptions.includes('keepFunctionNames'), - removeComments: additionalOptions.includes('removeComments'), - sourceMaps: additionalOptions.includes('sourceMaps'), - maxOptimize: additionalOptions.includes('maxOptimize'), - standalone: additionalOptions.includes('standalone') + keepFunctionNames: additionalOptions.includes("keepFunctionNames"), + removeComments: additionalOptions.includes("removeComments"), + sourceMaps: additionalOptions.includes("sourceMaps"), + maxOptimize: additionalOptions.includes("maxOptimize"), + standalone: additionalOptions.includes("standalone"), }; } catch (err) { if (err.name === "ExitPromptError") { diff --git a/editing/scripts/dev.js b/editing/scripts/dev.js index ad541b6..b3d3b5d 100644 --- a/editing/scripts/dev.js +++ b/editing/scripts/dev.js @@ -6,84 +6,89 @@ import path from "path"; import fs from "fs"; try { - // Get the user's project directory - const projectDir = process.cwd(); - - // Check if we're in a Discraft project - if (!fs.existsSync(path.join(projectDir, "src"))) { - error("No src/ directory found. Please ensure you are in a Discraft project directory."); - process.exit(1); - } + // Get the user's project directory + const projectDir = process.cwd(); - const srcDir = path.join(projectDir, "src"); + // Check if we're in a Discraft project + if (!fs.existsSync(path.join(projectDir, "src"))) { + error( + "No src/ directory found. Please ensure you are in a Discraft project directory.", + ); + process.exit(1); + } - // On startup, generate commands and events - generateCommands(srcDir); - generateEvents(srcDir); + const srcDir = path.join(projectDir, "src"); - const mon = nodemon({ - exec: "node -r dotenv/config", - script: path.join(srcDir, "index.js"), - watch: [srcDir], - ext: "js", - env: { "NODE_ENV": "development" } - }); + // On startup, generate commands and events + generateCommands(srcDir); + generateEvents(srcDir); - // Handle normal exits - mon.on("quit", () => { - info("Development mode terminated"); - process.exit(); - }); + const mon = nodemon({ + exec: "node -r dotenv/config", + script: path.join(srcDir, "index.js"), + watch: [srcDir], + ext: "js", + env: { NODE_ENV: "development" }, + }); - // Handle errors - mon.on("error", (err) => { - error("Nodemon error:", err); - }); + // Handle normal exits + mon.on("quit", () => { + info("Development mode terminated"); + process.exit(); + }); - // Log restart information - mon.on("restart", (files) => { - const restartTime = Date.now(); - info(`Restarting due to changes in ${files.length} files...`); - generateCommands(srcDir); - generateEvents(srcDir); - success(`Restart complete in ${Date.now() - restartTime}ms`); - }); + // Handle errors + mon.on("error", (err) => { + error("Nodemon error:", err); + }); - // Handle process signals - process.on("SIGINT", () => { - warn("Received SIGINT. Gracefully shutting down..."); - mon.emit("quit"); - }); + // Log restart information + mon.on("restart", (files) => { + const restartTime = Date.now(); + info(`Restarting due to changes in ${files.length} files...`); + generateCommands(srcDir); + generateEvents(srcDir); + success(`Restart complete in ${Date.now() - restartTime}ms`); + }); - process.on("SIGTERM", () => { - warn("Received SIGTERM. Gracefully shutting down..."); - mon.emit("quit"); - }); + // Handle process signals + process.on("SIGINT", () => { + warn("Received SIGINT. Gracefully shutting down..."); + mon.emit("quit"); + }); - // Log startup - mon.on("start", () => { - info("Starting development mode..."); - }); + process.on("SIGTERM", () => { + warn("Received SIGTERM. Gracefully shutting down..."); + mon.emit("quit"); + }); - // Handle stdout/stderr from child process - mon.on("stdout", (data) => { - process.stdout.write(data); - }); + // Log startup + mon.on("start", () => { + info("Starting development mode..."); + }); - mon.on("stderr", (data) => { - process.stderr.write(data); - }); + // Handle stdout/stderr from child process + mon.on("stdout", (data) => { + process.stdout.write(data); + }); - // Handle crashes in the child process - mon.on("crash", () => { - error("Application crashed - waiting for file changes before restarting..."); - }); + mon.on("stderr", (data) => { + process.stderr.write(data); + }); + // Handle crashes in the child process + mon.on("crash", () => { + error( + "Application crashed - waiting for file changes before restarting...", + ); + }); } catch (err) { - if (err.code === "ENOENT") { - error("Could not determine current working directory. Please ensure you are in a valid directory with proper permissions."); - } else { - error("Failed to start development server:", err); - } - process.exit(1); + if (err.code === "ENOENT") { + error( + "Could not determine current working directory. Please ensure you are in a valid directory with proper permissions.", + ); + } else { + error("Failed to start development server:", err); + } + process.exit(1); } diff --git a/editing/scripts/start.js b/editing/scripts/start.js index c069ee0..1a6d9b9 100644 --- a/editing/scripts/start.js +++ b/editing/scripts/start.js @@ -4,13 +4,13 @@ import fs from "fs"; import path from "path"; const projectDir = process.cwd(); -const distDir = process.argv[process.argv.length-1]; -const distPath = path.join(projectDir, distDir); +const distDir = process.argv[process.argv.length - 1]; +const distPath = path.join(projectDir, distDir); const bundlePath = path.join(distPath, "bundle.js"); // Check if dist directory and bundle.js exist if (!fs.existsSync(distPath) || !fs.existsSync(bundlePath)) { - error("Build not found! Please run \"discraft build\" first"); + error('Build not found! Please run "discraft build" first'); info("You can create a production build by running: discraft build"); process.exit(1); } @@ -19,7 +19,7 @@ info("Starting bot in production mode..."); const bot = spawn("node", ["-r", "dotenv/config", bundlePath], { stdio: "inherit", - cwd: projectDir + cwd: projectDir, }); bot.on("error", (err) => { diff --git a/editing/scripts/test-token.js b/editing/scripts/test-token.js index 3aa50b4..aef3f6a 100644 --- a/editing/scripts/test-token.js +++ b/editing/scripts/test-token.js @@ -8,7 +8,7 @@ const token = process.env.BOT_TOKEN; if (!token) { error( - "BOT_TOKEN is not set in the environment variables. Make sure you are in the root of your project and have an environment file (like .env) with the bot token." + "BOT_TOKEN is not set in the environment variables. Make sure you are in the root of your project and have an environment file (like .env) with the bot token.", ); process.exit(1); } diff --git a/editing/scripts/utils/fileSizeUtil.js b/editing/scripts/utils/fileSizeUtil.js index 256bd11..f752ea6 100644 --- a/editing/scripts/utils/fileSizeUtil.js +++ b/editing/scripts/utils/fileSizeUtil.js @@ -3,48 +3,49 @@ import path from "path"; import { info, success } from "../../common/utils/logger.js"; export function formatBytes(bytes) { - if (bytes === 0) return "0 Bytes"; - const k = 1024; - const sizes = ["Bytes", "KB", "MB", "GB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; + if (bytes === 0) return "0 Bytes"; + const k = 1024; + const sizes = ["Bytes", "KB", "MB", "GB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; } export async function getFileSizes(dir) { - const files = await fs.promises.readdir(dir, { withFileTypes: true }); - let totalSize = 0; + const files = await fs.promises.readdir(dir, { withFileTypes: true }); + let totalSize = 0; - for (const file of files) { - const fullPath = path.join(dir, file.name); - if (file.isDirectory()) { - totalSize += await getFileSizes(fullPath); - } else if (file.name.endsWith(".js")) { - const stats = await fs.promises.stat(fullPath); - totalSize += stats.size; - } + for (const file of files) { + const fullPath = path.join(dir, file.name); + if (file.isDirectory()) { + totalSize += await getFileSizes(fullPath); + } else if (file.name.endsWith(".js")) { + const stats = await fs.promises.stat(fullPath); + totalSize += stats.size; } - return totalSize; + } + return totalSize; } export async function displaySizeComparison(srcSize, distPath) { - // Get bundle size - const bundlePath = path.join(distPath, "bundle.js"); - const bundleSize = fs.existsSync(bundlePath) ? - (await fs.promises.stat(bundlePath)).size : 0; + // Get bundle size + const bundlePath = path.join(distPath, "bundle.js"); + const bundleSize = fs.existsSync(bundlePath) + ? (await fs.promises.stat(bundlePath)).size + : 0; - const reduction = srcSize > 0 ? - ((srcSize - bundleSize) / srcSize * 100).toFixed(1) : 0; + const reduction = + srcSize > 0 ? (((srcSize - bundleSize) / srcSize) * 100).toFixed(1) : 0; - info("\nBuild Statistics:"); - info("================"); - info("Source Files:"); - info(` Original Size: ${formatBytes(srcSize)}`); - info("\nBundle:"); - info(` Final Size: ${formatBytes(bundleSize)}`); - info(` Size Reduction: ${reduction}%\n`); + info("\nBuild Statistics:"); + info("================"); + info("Source Files:"); + info(` Original Size: ${formatBytes(srcSize)}`); + info("\nBundle:"); + info(` Final Size: ${formatBytes(bundleSize)}`); + info(` Size Reduction: ${reduction}%\n`); - success("\nTotal Results:"); - success(`Original Size: ${formatBytes(srcSize)}`); - success(`Final Size : ${formatBytes(bundleSize)}`); - success(`Total Saved : ${reduction}%`); + success("\nTotal Results:"); + success(`Original Size: ${formatBytes(srcSize)}`); + success(`Final Size : ${formatBytes(bundleSize)}`); + success(`Total Saved : ${reduction}%`); } diff --git a/editing/scripts/utils/minifyUtilTerser.js b/editing/scripts/utils/minifyUtilTerser.js index 81d91ff..4198adc 100644 --- a/editing/scripts/utils/minifyUtilTerser.js +++ b/editing/scripts/utils/minifyUtilTerser.js @@ -3,78 +3,87 @@ import fs from "fs"; import { minify } from "terser"; export async function minifyWithTerser(filePath, config) { - const code = await fs.promises.readFile(filePath, "utf8"); + const code = await fs.promises.readFile(filePath, "utf8"); - const terserOptions = { - module: true, - toplevel: true, - compress: { - ecma: 2020, - module: true, - toplevel: true, - passes: (config.maxOptimize ? 3 : 1), - keep_fnames: config.standalone ? true : config.keepFunctionNames, - pure_getters: true, - dead_code: true, - unused: true, - properties: !config.standalone, - drop_debugger: !config.standalone, - arguments: !config.standalone, - booleans_as_integers: false, - hoist_funs: !config.standalone, - hoist_props: !config.standalone, - hoist_vars: !config.standalone, - join_vars: true, - negate_iife: !config.standalone, - reduce_vars: true, - collapse_vars: !config.standalone, - inline: config.standalone ? 1 : 3, - evaluate: true, - pure_funcs: ["console.log", "console.debug"], - drop_console: false, - sequences: true, - unsafe_math: false, - unsafe_methods: false, - unsafe_proto: false, - unsafe_regexp: false, - unsafe_undefined: false, + const terserOptions = { + module: true, + toplevel: true, + compress: { + ecma: 2020, + module: true, + toplevel: true, + passes: config.maxOptimize ? 3 : 1, + keep_fnames: config.standalone ? true : config.keepFunctionNames, + pure_getters: true, + dead_code: true, + unused: true, + properties: !config.standalone, + drop_debugger: !config.standalone, + arguments: !config.standalone, + booleans_as_integers: false, + hoist_funs: !config.standalone, + hoist_props: !config.standalone, + hoist_vars: !config.standalone, + join_vars: true, + negate_iife: !config.standalone, + reduce_vars: true, + collapse_vars: !config.standalone, + inline: config.standalone ? 1 : 3, + evaluate: true, + pure_funcs: ["console.log", "console.debug"], + drop_console: false, + sequences: true, + unsafe_math: false, + unsafe_methods: false, + unsafe_proto: false, + unsafe_regexp: false, + unsafe_undefined: false, + }, + mangle: config.standalone + ? { + module: true, + toplevel: false, + keep_fnames: true, + properties: false, + } + : { + module: true, + toplevel: true, + keep_fnames: config.keepFunctionNames, + properties: config.maxOptimize + ? { + reserved: [ + "_events", + "_eventsCount", + "_maxListeners", + "domain", + ], + regex: /^_/, + } + : false, }, - mangle: config.standalone ? { - module: true, - toplevel: false, - keep_fnames: true, - properties: false - } : { - module: true, - toplevel: true, - keep_fnames: config.keepFunctionNames, - properties: config.maxOptimize ? { - reserved: ["_events", "_eventsCount", "_maxListeners", "domain"], - regex: /^_/ - } : false - }, - format: { - ecma: 2020, - comments: !config.removeComments, - ascii_only: true, - beautify: false, - indent_level: 0, - wrap_iife: false, - preserve_annotations: true, - max_line_len: false - }, - sourceMap: config.sourceMaps, - ie8: false, - safari10: false - }; + format: { + ecma: 2020, + comments: !config.removeComments, + ascii_only: true, + beautify: false, + indent_level: 0, + wrap_iife: false, + preserve_annotations: true, + max_line_len: false, + }, + sourceMap: config.sourceMaps, + ie8: false, + safari10: false, + }; - const result = await minify(code, terserOptions); - if (result.error) { - throw result.error; - } + const result = await minify(code, terserOptions); + if (result.error) { + throw result.error; + } - await fs.promises.writeFile(filePath, result.code); - if (result.map && (config.sourceMaps)) { - await fs.promises.writeFile(filePath + ".map", result.map); - } -} \ No newline at end of file + await fs.promises.writeFile(filePath, result.code); + if (result.map && config.sourceMaps) { + await fs.promises.writeFile(filePath + ".map", result.map); + } +} diff --git a/editing/src/discraft/commands/handler.js b/editing/src/discraft/commands/handler.js index 5897ab8..8e16cea 100644 --- a/editing/src/discraft/commands/handler.js +++ b/editing/src/discraft/commands/handler.js @@ -26,7 +26,7 @@ export class CommandHandler { if (command.cacheable) { const cachedResult = commandCache.get( interaction.commandName, - interaction.options.data + interaction.options.data, ); if (cachedResult) { @@ -66,7 +66,7 @@ export class CommandHandler { commandCache.set( interaction.commandName, interaction.options.data, - result + result, ); } } catch (err) { @@ -101,7 +101,7 @@ export class CommandHandler { debug( `Time to register commands: ${ Date.now() - this.client.readyTimestamp - }ms` + }ms`, ); success(`Time to online: ${Date.now() - this.serverStartTime}ms`); }); @@ -120,7 +120,7 @@ export class CommandHandler { debug(`Loaded command: ${command.data.name}`); } else { error( - `The command ${name} is missing required "data" or "execute" property.` + `The command ${name} is missing required "data" or "execute" property.`, ); } } @@ -133,7 +133,7 @@ export class CommandHandler { const rest = new REST().setToken(token); try { debug( - `Started refreshing ${this.commandsData.length} application (/) commands.` + `Started refreshing ${this.commandsData.length} application (/) commands.`, ); // Register commands globally diff --git a/editing/src/utils/commandCache.js b/editing/src/utils/commandCache.js index 12d0160..7e01e12 100644 --- a/editing/src/utils/commandCache.js +++ b/editing/src/utils/commandCache.js @@ -144,7 +144,7 @@ class CommandCache { size: this.cache.size, maxSize: this.maxSize, memoryUsage: `${(this.getCurrentMemoryUsage() / (1024 * 1024)).toFixed( - 2 + 2, )}MB`, maxMemory: `${this.maxMemoryMB}MB`, }; diff --git a/editing/src/utils/logger.js b/editing/src/utils/logger.js index cd0adfb..5cf1786 100644 --- a/editing/src/utils/logger.js +++ b/editing/src/utils/logger.js @@ -52,7 +52,7 @@ export const error = (message, ...args) => { export const trace = (message, ...args) => { console.trace( `${colors.fg.crimson}[TRACE]${colors.reset} ${message}`, - ...args + ...args, ); }; diff --git a/eslint.config.js b/eslint.config.js index b64daea..255d701 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,25 +1,25 @@ -import js from '@eslint/js'; -import globals from 'globals'; -import importPlugin from 'eslint-plugin-import'; -import promisePlugin from 'eslint-plugin-promise'; -import nodePlugin from 'eslint-plugin-node'; +import js from "@eslint/js"; +import globals from "globals"; +import importPlugin from "eslint-plugin-import"; +import promisePlugin from "eslint-plugin-promise"; +import nodePlugin from "eslint-plugin-node"; // Function to trim whitespace from global names const trimGlobals = (obj) => { return Object.fromEntries( - Object.entries(obj).map(([key, value]) => [key.trim(), value]) + Object.entries(obj).map(([key, value]) => [key.trim(), value]), ); }; export default [ { - files: ['**/*.js'], + files: ["**/*.js"], languageOptions: { ecmaVersion: 2022, - sourceType: 'module', + sourceType: "module", globals: { ...trimGlobals(globals.browser), - ...trimGlobals(globals.node) + ...trimGlobals(globals.node), }, }, plugins: { @@ -30,15 +30,15 @@ export default [ }, rules: { ...js.configs.recommended.rules, - 'import/named': 'error', - 'promise/always-return': 'error', - 'promise/no-return-wrap': 'error', - 'no-unused-vars': 'warn', - 'consistent-return': 'error', - 'camelcase': 'warn', + "import/named": "error", + "promise/always-return": "error", + "promise/no-return-wrap": "error", + "no-unused-vars": "warn", + "consistent-return": "error", + camelcase: "warn", }, }, { - ignores: ['dist/**', 'node_modules/**'], + ignores: ["dist/**", "node_modules/**"], }, -] \ No newline at end of file +]; diff --git a/package.json b/package.json index 4c841e5..4efa6fd 100644 --- a/package.json +++ b/package.json @@ -47,16 +47,17 @@ "@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-replace": "^6.0.1", "commander": "^12.1.0", + "consola": "^3.2.3", "discord.js": "^14.16.3", "dotenv": "^16.4.7", "figlet": "^1.8.0", "nodemon": "^3.1.7", "rollup": "^4.28.1", - "terser": "^5.37.0", - "inquirer": "^12.2.0" + "terser": "^5.37.0" }, "devDependencies": { "@eslint/js": "^9.16.0", + "@types/figlet": "^1.7.0", "esbuild": "^0.24.0", "eslint": "^9.16.0", "eslint-plugin-import": "^2.31.0", diff --git a/scripts/add-event.js b/scripts/add-event.js index 3b89c16..0d51eb2 100644 --- a/scripts/add-event.js +++ b/scripts/add-event.js @@ -1 +1 @@ -import fs from"fs";import path from"path";import{search,input}from"@inquirer/prompts";import{success,error}from"../common/utils/logger.js";import{Events}from"discord.js";async function generateEvent(){const e=fs.existsSync(path.join(process.cwd(),"src")),r=process.cwd().endsWith("src");e||(r?(error('You are in the "src/" directory. You should be in the root of your Discraft project.'),process.exit(1)):(error('The "src/" directory does not exist. Please run "discraft init" to initialize a project, or ensure you are in the root of your Discraft project.'),process.exit(1)));const t={name:"",type:""};try{if(t.name=await input({message:"Event name:",required:!0,validate:e=>!!/^[a-z]+(-[a-z]+)*$/.test(e)||"Must be lowercase with single dashes only."}),t.type=await search({message:"Event type:",required:!0,source:async e=>{const r=Object.keys(Events).map((e=>({name:e,value:e})));return e?r.filter((r=>r.name.toLowerCase().includes(e.toLowerCase()))).map((e=>({name:`${e.name}`,value:e.value}))):r}}),"custom"===t.type){const e=await input({message:"Custom event name (from Discord.js Events):",required:!0,validate:e=>e.length>0});t.type=e.customEventName}}catch(e){return"ExitPromptError"===e.name?(error("Cancelled by user."),process.exit(0)):(error("Error:",e),process.exit(1))}const s=path.join(process.cwd(),"src","events");fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0});const n=path.join(s,`${t.name}.js`),o=`import { debug, error } from "../utils/logger.js";\nimport { Events } from "discord.js";\n\nexport default (client) => {\n client.on(Events.${t.type}, (${"messageCreate"===t.type?"message":"event"}) => {\n debug("'${t.name}' event triggered");\n try {\n // Add your event handling logic here\n \n } catch (err) {\n error("Error in '${t.name}' event handler:", err);\n }\n });\n};\n`;try{return fs.writeFileSync(n,o),success(`Created event handler at src/events/${t.name}.js\nEvent will trigger on: ${t.type}`),{name:t.name}}catch(e){return error("Error creating event file:",e),process.exit(1)}}generateEvent().catch((e=>{error("Error creating event:",e),process.exit(1)})); \ No newline at end of file +import fs from"fs";import path from"path";import{search,input}from"@inquirer/prompts";import{success,error}from"../common/utils/logger.js";import{Events}from"discord.js";async function generateEvent(){const e=fs.existsSync(path.join(process.cwd(),"src")),r=process.cwd().endsWith("src");e||(r?(error('You are in the "src/" directory. You should be in the root of your Discraft project.'),process.exit(1)):(error('The "src/" directory does not exist. Please run "discraft init" to initialize a project, or ensure you are in the root of your Discraft project.'),process.exit(1)));const t={name:"",type:""};try{if(t.name=await input({message:"Event name:",required:!0,validate:e=>!!/^[a-z]+(-[a-z]+)*$/.test(e)||"Must be lowercase with single dashes only."}),t.type=await search({message:"Event type:",required:!0,source:async e=>{const r=Object.keys(Events).map((e=>({name:e,value:e})));return e?r.filter((r=>r.name.toLowerCase().includes(e.toLowerCase()))).map((e=>({name:`${e.name}`,value:e.value}))):r}}),"custom"===t.type){const e=await input({message:"Custom event name (from Discord.js Events):",required:!0,validate:e=>e.length>0});t.type=e.customEventName}}catch(e){return"ExitPromptError"===e.name?(error("Cancelled by user."),process.exit(0)):(error("Error:",e),process.exit(1))}const s=path.join(process.cwd(),"src","events");fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0});const n=path.join(s,`${t.name}.js`),o=`import { debug, error } from "../utils/logger.js";\nimport { Events } from "discord.js";\n\nexport default (client) => {\n client.on(Events.${t.type}, (${"messageCreate"===t.type?"message":"event"}) => {\n debug("'${t.name}' event triggered");\n try {\n // Add your event handling logic here\n\n } catch (err) {\n error("Error in '${t.name}' event handler:", err);\n }\n });\n};\n`;try{return fs.writeFileSync(n,o),success(`Created event handler at src/events/${t.name}.js\nEvent will trigger on: ${t.type}`),{name:t.name}}catch(e){return error("Error creating event file:",e),process.exit(1)}}generateEvent().catch((e=>{error("Error creating event:",e),process.exit(1)})); \ No newline at end of file diff --git a/scripts/build.js b/scripts/build.js index 6d09ada..750f8b5 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1 +1 @@ -import fs from"fs";import path from"path";import{info,error,success,log}from"../common/utils/logger.js";import{checkbox,confirm}from"@inquirer/prompts";import{rollup}from"rollup";import{getFileSizes,displaySizeComparison}from"./utils/fileSizeUtil.js";import{minifyWithTerser}from"./utils/minifyUtilTerser.js";import generateCommands from"./compile/genCommands.js";import generateEvents from"./compile/genEvents.js";import{nodeResolve}from"@rollup/plugin-node-resolve";import commonjs from"@rollup/plugin-commonjs";import json from"@rollup/plugin-json";import replace from"@rollup/plugin-replace";import babel from"@rollup/plugin-babel";import{exec}from"child_process";const projectDir=process.cwd(),srcDir=path.join(projectDir,"src");let buildInProgress=!1,currentBundle=null;async function analyzeDependencies(e,n){const r=new Set;for(const e of n.output){const n=Array.isArray(e.output)?e.output:[e];for(const e of n)e.imports&&e.imports.forEach((e=>r.add(e)))}const o={name:"discraft-bot",type:"module",version:"1.0.0",description:"Bot created with Discraft",main:"bundle.js",dependencies:{}};return r.forEach((e=>{o.dependencies[e]="latest"})),info(`Found ${r.size} external dependencies.`),o}async function build(e){buildInProgress=!0;try{if(info("Starting build process..."),!fs.existsSync(srcDir))throw new Error(`Source directory not found at ${srcDir}`);const n=path.join(srcDir,"index.js");if(!fs.existsSync(n))throw new Error(`Entry point not found at ${n}`);info("Generating commands and events..."),await new Promise((e=>{generateCommands(srcDir),generateEvents(srcDir),e()}));const r=await getBuildConfig(e),o=Date.now(),i=await getFileSizes(srcDir),s=path.resolve(projectDir,e.output);fs.existsSync(s)&&await fs.promises.rm(s,{recursive:!0}),await fs.promises.mkdir(s,{recursive:!0});const t={input:n,output:{file:path.join(s,"bundle.js"),format:"es",exports:"auto",minifyInternalExports:!0},external:r.standalone?[]:e=>!(e.startsWith(".")||e.startsWith("/")||e.startsWith("src/")||e.startsWith("../")||e.startsWith("./")),plugins:[replace({preventAssignment:!0,"process.env.NODE_ENV":JSON.stringify("production")}),nodeResolve({preferBuiltins:!0,exportConditions:["node"]}),commonjs({ignoreDynamicRequires:!1}),json(),babel({babelHelpers:"bundled",configFile:!1,babelrc:!1,presets:[["@babel/preset-env",{targets:{node:"current"},modules:!1,loose:!0,exclude:["transform-typeof-symbol"]}]]})],treeshake:{moduleSideEffects:!1,propertyReadSideEffects:!1,tryCatchDeoptimization:!1}};info("Running Rollup bundler..."),currentBundle=await rollup(t);const a=await currentBundle.write(t.output);await currentBundle.close(),currentBundle=null;const c=path.join(s,"bundle.js");if(!fs.existsSync(c))throw new Error(`Bundle file not created at ${c}`);if(r.standalone)info("Skipping package.json generation (standalone mode)");else{const e=await analyzeDependencies(c,{output:[a]});await fs.promises.writeFile(path.join(s,"package.json"),JSON.stringify(e,null,2)),info("Generated package.json with dependencies")}r.minify&&(info("Running Terser minification..."),r.maxOptimize&&info("Using maximum optimization settings (this may take longer)..."),await minifyWithTerser(c,r)),r.standalone||await displaySizeComparison(i,s),info(`Output location: ${s}`),success("Build completed successfully in "+(Date.now()-o)+"ms")}catch(e){"ExitPromptError"===e.name?error("Build cancelled"):e instanceof Error?error("Build failed or cancelled:",e?.message||e):error("Build failed or cancelled:",e),process.exit(1)}finally{buildInProgress=!1,currentBundle=null}}async function getBuildConfig(e){try{if(e.yes)return{minify:!0,keepFunctionNames:!1,removeComments:!0,sourceMaps:!1,maxOptimize:e.maxOptimize,standalone:e.standalone};console.log("\n");const n=await confirm({message:"Do you want to minify the code?"});let r=[];return n&&(r=await checkbox({message:"Configure Additional Build Options:",choices:[{value:"maxOptimize",name:"Enable maximum optimization",checked:!0},{value:"keepFunctionNames",name:"Keep function names for better error traces",checked:!1},{value:"removeComments",name:"Remove comments from the output",checked:!0},{value:"sourceMaps",name:"Generate source maps",checked:!1},{value:"standalone",name:"Create standalone bundle with all dependencies included",checked:!1}]})),{minify:n,keepFunctionNames:r.includes("keepFunctionNames"),removeComments:r.includes("removeComments"),sourceMaps:r.includes("sourceMaps"),maxOptimize:r.includes("maxOptimize"),standalone:r.includes("standalone")}}catch(e){return"ExitPromptError"===e.name?(error("Build cancelled by user."),process.exit(0)):(error("Error while getting build config:",e),process.exit(1))}}process.on("SIGINT",(async()=>{if(buildInProgress){info("\nGracefully cancelling build process...");try{currentBundle&&await currentBundle.close();const e=path.resolve(projectDir,"dist");fs.existsSync(e)&&await fs.promises.rm(e,{recursive:!0}),info("Build cancelled and cleaned up successfully.")}catch(e){error("Error while cleaning up:",e)}}process.exit(0)}));const options={yes:process.argv.includes("-y")||process.argv.includes("--yes"),output:process.argv.includes("-o")?process.argv[process.argv.indexOf("-o")+1]:process.argv.includes("--output")?process.argv[process.argv.indexOf("--output")+1]:"dist",maxOptimize:process.argv.includes("--max-optimize"),standalone:process.argv.includes("--standalone")};build(options); \ No newline at end of file +import fs from"fs";import path from"path";import{info,error,success}from"../common/utils/logger.js";import{checkbox,confirm}from"@inquirer/prompts";import{rollup}from"rollup";import{getFileSizes,displaySizeComparison}from"./utils/fileSizeUtil.js";import{minifyWithTerser}from"./utils/minifyUtilTerser.js";import generateCommands from"./compile/genCommands.js";import generateEvents from"./compile/genEvents.js";import{nodeResolve}from"@rollup/plugin-node-resolve";import commonjs from"@rollup/plugin-commonjs";import json from"@rollup/plugin-json";import replace from"@rollup/plugin-replace";import babel from"@rollup/plugin-babel";const projectDir=process.cwd(),srcDir=path.join(projectDir,"src");let buildInProgress=!1,currentBundle=null;async function analyzeDependencies(e,n){const r=new Set;for(const e of n.output){const n=Array.isArray(e.output)?e.output:[e];for(const e of n)e.imports&&e.imports.forEach((e=>r.add(e)))}const o={name:"discraft-bot",type:"module",version:"1.0.0",description:"Bot created with Discraft",main:"bundle.js",dependencies:{}};return r.forEach((e=>{o.dependencies[e]="latest"})),info(`Found ${r.size} external dependencies.`),o}async function build(e){buildInProgress=!0;try{if(info("Starting build process..."),!fs.existsSync(srcDir))throw new Error(`Source directory not found at ${srcDir}`);const n=path.join(srcDir,"index.js");if(!fs.existsSync(n))throw new Error(`Entry point not found at ${n}`);info("Generating commands and events..."),await new Promise((e=>{generateCommands(srcDir),generateEvents(srcDir),e()}));const r=await getBuildConfig(e),o=Date.now(),i=await getFileSizes(srcDir),s=path.resolve(projectDir,e.output);fs.existsSync(s)&&await fs.promises.rm(s,{recursive:!0}),await fs.promises.mkdir(s,{recursive:!0});const t={input:n,output:{file:path.join(s,"bundle.js"),format:"es",exports:"auto",minifyInternalExports:!0},external:r.standalone?[]:e=>!(e.startsWith(".")||e.startsWith("/")||e.startsWith("src/")||e.startsWith("../")||e.startsWith("./")),plugins:[replace({preventAssignment:!0,"process.env.NODE_ENV":JSON.stringify("production")}),nodeResolve({preferBuiltins:!0,exportConditions:["node"]}),commonjs({ignoreDynamicRequires:!1}),json(),babel({babelHelpers:"bundled",configFile:!1,babelrc:!1,presets:[["@babel/preset-env",{targets:{node:"current"},modules:!1,loose:!0,exclude:["transform-typeof-symbol"]}]]})],treeshake:{moduleSideEffects:!1,propertyReadSideEffects:!1,tryCatchDeoptimization:!1}};info("Running Rollup bundler..."),currentBundle=await rollup(t);const a=await currentBundle.write(t.output);await currentBundle.close(),currentBundle=null;const c=path.join(s,"bundle.js");if(!fs.existsSync(c))throw new Error(`Bundle file not created at ${c}`);if(r.standalone)info("Skipping package.json generation (standalone mode)");else{const e=await analyzeDependencies(c,{output:[a]});await fs.promises.writeFile(path.join(s,"package.json"),JSON.stringify(e,null,2)),info("Generated package.json with dependencies")}r.minify&&(info("Running Terser minification..."),r.maxOptimize&&info("Using maximum optimization settings (this may take longer)..."),await minifyWithTerser(c,r)),r.standalone||await displaySizeComparison(i,s),info(`Output location: ${s}`),success("Build completed successfully in "+(Date.now()-o)+"ms")}catch(e){"ExitPromptError"===e.name?error("Build cancelled"):e instanceof Error?error("Build failed or cancelled:",e?.message||e):error("Build failed or cancelled:",e),process.exit(1)}finally{buildInProgress=!1,currentBundle=null}}async function getBuildConfig(e){try{if(e.yes)return{minify:!0,keepFunctionNames:!1,removeComments:!0,sourceMaps:!1,maxOptimize:e.maxOptimize,standalone:e.standalone};console.log("\n");const n=await confirm({message:"Do you want to minify the code?"});let r=[];return n&&(r=await checkbox({message:"Configure Additional Build Options:",choices:[{value:"maxOptimize",name:"Enable maximum optimization",checked:!0},{value:"keepFunctionNames",name:"Keep function names for better error traces",checked:!1},{value:"removeComments",name:"Remove comments from the output",checked:!0},{value:"sourceMaps",name:"Generate source maps",checked:!1},{value:"standalone",name:"Create standalone bundle with all dependencies included",checked:!1}]})),{minify:n,keepFunctionNames:r.includes("keepFunctionNames"),removeComments:r.includes("removeComments"),sourceMaps:r.includes("sourceMaps"),maxOptimize:r.includes("maxOptimize"),standalone:r.includes("standalone")}}catch(e){return"ExitPromptError"===e.name?(error("Build cancelled by user."),process.exit(0)):(error("Error while getting build config:",e),process.exit(1))}}process.on("SIGINT",(async()=>{if(buildInProgress){info("\nGracefully cancelling build process...");try{currentBundle&&await currentBundle.close();const e=path.resolve(projectDir,"dist");fs.existsSync(e)&&await fs.promises.rm(e,{recursive:!0}),info("Build cancelled and cleaned up successfully.")}catch(e){error("Error while cleaning up:",e)}}process.exit(0)}));const options={yes:process.argv.includes("-y")||process.argv.includes("--yes"),output:process.argv.includes("-o")?process.argv[process.argv.indexOf("-o")+1]:process.argv.includes("--output")?process.argv[process.argv.indexOf("--output")+1]:"dist",maxOptimize:process.argv.includes("--max-optimize"),standalone:process.argv.includes("--standalone")};build(options); \ No newline at end of file