import fs from "node:fs/promises" interface Context { args: Array commands: Array command: string } interface Response { code: number } export interface Command { run(input: Context): Promise | Response name: string description?: string } const builtinCommands: Array = [ { name: "help", run({ commands }) { console.table( commands.map((command) => ({ name: command.name, description: command.description ?? "no description", })), ); return { code: 0, }; }, }, ]; async function createContext(): Promise { const ctx = { args: process.argv.slice(3), commands: await getCommands(), command: process.argv[2] ?? "help", }; return ctx } async function listfiles(folder: string): Promise> { const files = await fs.readdir(folder) const res: Array = []; for (const file of files) { const path = `${folder}/${file}` if ((await fs.stat(path)).isDirectory()) { res.push(...(await listfiles(path))) } else { res.push(path) } } return res } async function getCommands(): Promise> { const files = (await listfiles(__dirname)) .filter((it) => it !== `${__dirname}/index.ts`) .map((it) => import(it).then((it) => it.default)) return builtinCommands.concat(await Promise.all(files)) } ;(async () => { const context = await createContext() for (const command of context.commands) { if (command.name === context.command) { const res = await command.run(context) process.exit(res.code) } } console.log( `command "${context.command}" not found, please use "help" to get the list of commands`, ) })()