diff --git a/index.js b/index.js index 75a1596..1640b43 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,3 @@ #!/usr/bin/env node -import "./dist/index.mjs" +import "./dist/index.js" diff --git a/package.json b/package.json index 5e5afdd..1989e05 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,18 @@ "version": "1.0.0", "main": "./dist/index.js", "license": "MIT", + "scripts": { + "build": "tsc" + }, "bin": { - "@sern/create-bot": "index.js" + "@sern/create-bot": "./dist/index.js" }, "devDependencies": { "@types/minimist": "^1.2.2", "@types/prompts": "^2.4.4", "colorette": "^2.0.20", "minimist": "^1.2.8", - "prompts": "^2.4.2" + "prompts": "^2.4.2", + "typescript": "^5.0.0" } } diff --git a/src/index.ts b/src/index.ts index 4766a4b..31a416e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,33 +1,36 @@ -import { greenBright, red, redBright } from 'colorette'; +import { greenBright, red } from 'colorette'; import prompt, { PromptObject } from 'prompts'; -import minimist from 'minimist' -import { writeFile } from 'fs/promises' -import path from 'path' -import fs from 'fs' +import minimist from 'minimist'; +import path from 'path'; +import fs from 'fs'; +import assert from 'node:assert'; + const argv = minimist<{ - template?: string -}>(process.argv.slice(2)); + template?: string; + name?: string; + overwrite?: boolean; +}>(process.argv.slice(2), { boolean: true }); const cwd = process.cwd(); const templateChoices = [ { - title: 'typescript' + title: 'ts' }, { - title: 'typescript-esm' + title: 'ts-esm' }, { - title: 'javascript' + title: 'js' }, { - title: 'javascript-esm' + title: 'js-esm' }, ]; const template: PromptObject = { message: 'Choose template', name: 'template', type: 'select', - choices: templateChoices.map(t => ({ title: t.title, value: t.title }) ) + choices: templateChoices.map(t => ({ title: t.title, value: `template-${t.title}` }) ) }; const name: PromptObject = { message: 'What is your project name?', @@ -52,11 +55,9 @@ async function runInteractive() { }, ); const root = path.join(cwd, result.name); - - - let overwrite = false; - if (overwrite) { - emptyDir(root) + const selectedTemplate = path.join(cwd, result.template); + if (argv.overwrite) { + emptyDir(root); } else if (!fs.existsSync(root)) { fs.mkdirSync(root, { recursive: true }) } @@ -70,19 +71,45 @@ async function runInteractive() { cmds_dir: 'commands' }, }; - //console.log(greenBright('Writing sern.config.json to '+ result.name + "/sern.config.json")); - -// try{ -// await writeFile(path.join(cwd, result.name, 'sern.config.json'), JSON.stringify(configJson), 'utf8'); -// } catch(E) { -// console.error(redBright(E)); -// process.exit(1); -// }; + console.log(greenBright(`overwrite: ${argv.overwrite ?? false};\ncopy: ${selectedTemplate} ${root}`)); + await copyFolderRecursiveAsync(selectedTemplate , root); + console.log(greenBright('Writing sern.config.json to '+ result.name + "/sern.config.json")); + await fs.promises.writeFile(path.join(root, 'sern.config.json'), JSON.stringify(configJson), 'utf8'); + + + console.log(greenBright('Done! visit https://sern.dev for documentation and join https://sern.dev/discord! Happy hacking :)' )); } -async function runShort() { +async function runShort(templateName: string, name:string) { + const fullTemplateName = `template-${templateName}`; + const doesTemplateExist = templateChoices + .some(tName => tName.title.localeCompare(fullTemplateName, undefined, { sensitivity: 'base'})); + if(!doesTemplateExist) { + throw new Error(red('✖') + ' Could not find template: ' + templateName); + } + const root = path.join(cwd, name); + const selectedTemplate = path.join(cwd, fullTemplateName); + if (argv.overwrite) { + emptyDir(root); + } else if (!fs.existsSync(root)) { + fs.mkdirSync(root, { recursive: true }) + } + const isTypescript = templateName.includes('typescript'); + const configJson = { + language : isTypescript ? 'typescript' : 'javascript', + paths: { + base: 'src', + cmds_dir: 'commands' + }, + }; + console.log(greenBright(`overwrite: ${argv.overwrite ?? false};\ncopy: ${selectedTemplate} ${root}`)); + await copyFolderRecursiveAsync(selectedTemplate , root); + console.log(greenBright('Writing sern.config.json to '+ name + "/sern.config.json")); + + await fs.promises.writeFile(path.join(root, 'sern.config.json'), JSON.stringify(configJson), 'utf8'); + } async function init() { @@ -91,7 +118,9 @@ async function init() { if(!argv.template) { await runInteractive(); } else { - await runShort(); + assert(argv.name) + assert.match(argv.name, new RegExp('^(?:@[a-z0-9-*~][a-z0-9-*._~]*/)?[a-z0-9-~][a-z0-9-._~]*$', 'g')); + await runShort(argv.template, argv.name); } } @@ -107,5 +136,35 @@ function emptyDir(dir: string) { } } -init() +async function copyFolderRecursiveAsync(source: string, target: string) { + try { + // Create target folder if it doesn't exist + if (!fs.existsSync(target)) { + fs.mkdirSync(target); + } + + // Get all files and folders in the source folder + const files = await fs.promises.readdir(source); + + for (const file of files) { + const currentSource = path.join(source, file); + const currentTarget = path.join(target, file); + + // Check if the current item is a file or a folder + const stats = await fs.promises.stat(currentSource); + + if (stats.isDirectory()) { + // Recursively copy the subfolder + await copyFolderRecursiveAsync(currentSource, currentTarget); + } else { + // Copy the file + await fs.promises.copyFile(currentSource, currentTarget); + } + } + } catch (err) { + throw Error('An error occurred: '+ err); + } +} + +init(); diff --git a/template-js-esm/src/dependencies.d.ts b/template-js-esm/src/dependencies.d.ts new file mode 100644 index 0000000..e793aa0 --- /dev/null +++ b/template-js-esm/src/dependencies.d.ts @@ -0,0 +1,11 @@ +import { SernEmitter, Logging, CoreModuleStore, ModuleManager, ErrorHandling, CoreDependencies, Singleton } from '@sern/handler' +import { Client } from 'discord.js' + +declare global { + interface Dependencies extends CoreDependencies { + '@sern/client': Singleton + } +} + + +export {} diff --git a/template-js/src/dependencies.d.ts b/template-js/src/dependencies.d.ts new file mode 100644 index 0000000..e793aa0 --- /dev/null +++ b/template-js/src/dependencies.d.ts @@ -0,0 +1,11 @@ +import { SernEmitter, Logging, CoreModuleStore, ModuleManager, ErrorHandling, CoreDependencies, Singleton } from '@sern/handler' +import { Client } from 'discord.js' + +declare global { + interface Dependencies extends CoreDependencies { + '@sern/client': Singleton + } +} + + +export {} diff --git a/template-ts-esm/src/commands/ping.js b/template-ts-esm/src/commands/ping.js new file mode 100644 index 0000000..6490a59 --- /dev/null +++ b/template-ts-esm/src/commands/ping.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const handler_1 = require("@sern/handler"); +exports.default = (0, handler_1.commandModule)({ + type: handler_1.CommandType.Both, + plugins: [], + description: 'A ping command', + //alias : [], + execute: (ctx, args) => __awaiter(void 0, void 0, void 0, function* () { + yield ctx.reply('Pong 🏓'); + }), +}); diff --git a/template-ts-esm/src/dependencies.d.ts b/template-ts-esm/src/dependencies.d.ts new file mode 100644 index 0000000..e793aa0 --- /dev/null +++ b/template-ts-esm/src/dependencies.d.ts @@ -0,0 +1,11 @@ +import { SernEmitter, Logging, CoreModuleStore, ModuleManager, ErrorHandling, CoreDependencies, Singleton } from '@sern/handler' +import { Client } from 'discord.js' + +declare global { + interface Dependencies extends CoreDependencies { + '@sern/client': Singleton + } +} + + +export {} diff --git a/template-ts-esm/src/index.js b/template-ts-esm/src/index.js new file mode 100644 index 0000000..c3bdea6 --- /dev/null +++ b/template-ts-esm/src/index.js @@ -0,0 +1,31 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const discord_js_1 = require("discord.js"); +const handler_1 = require("@sern/handler"); +const client = new discord_js_1.Client({ + intents: [ + discord_js_1.GatewayIntentBits.Guilds, + discord_js_1.GatewayIntentBits.GuildMembers, + discord_js_1.GatewayIntentBits.GuildMessages, + discord_js_1.GatewayIntentBits.MessageContent, //Make sure this is enabled for text commands! + ], +}); +/** + * Where all of your dependencies are composed. + * '@sern/client' is usually your Discord Client. + * View documentation for pluggable dependencies + * Configure your dependency root to your liking. + * It follows the npm package iti https://itijs.org/. + * Use this function to access all of your dependencies. + * This is used for external event modules as well + */ +await (0, handler_1.makeDependencies)({ + build: (root) => root.add({ '@sern/client': (0, handler_1.single)(() => client) }) +}); +//View docs for all options +handler_1.Sern.init({ + defaultPrefix: '!', + commands: 'dist/commands', + // events: 'dist/events' (optional), +}); +client.login(); diff --git a/template-ts/src/commands/ping.js b/template-ts/src/commands/ping.js new file mode 100644 index 0000000..6490a59 --- /dev/null +++ b/template-ts/src/commands/ping.js @@ -0,0 +1,21 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const handler_1 = require("@sern/handler"); +exports.default = (0, handler_1.commandModule)({ + type: handler_1.CommandType.Both, + plugins: [], + description: 'A ping command', + //alias : [], + execute: (ctx, args) => __awaiter(void 0, void 0, void 0, function* () { + yield ctx.reply('Pong 🏓'); + }), +}); diff --git a/template-ts/src/dependencies.d.ts b/template-ts/src/dependencies.d.ts new file mode 100644 index 0000000..e793aa0 --- /dev/null +++ b/template-ts/src/dependencies.d.ts @@ -0,0 +1,11 @@ +import { SernEmitter, Logging, CoreModuleStore, ModuleManager, ErrorHandling, CoreDependencies, Singleton } from '@sern/handler' +import { Client } from 'discord.js' + +declare global { + interface Dependencies extends CoreDependencies { + '@sern/client': Singleton + } +} + + +export {} diff --git a/template-ts/src/index.js b/template-ts/src/index.js new file mode 100644 index 0000000..cfa4244 --- /dev/null +++ b/template-ts/src/index.js @@ -0,0 +1,44 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const discord_js_1 = require("discord.js"); +const handler_1 = require("@sern/handler"); +const client = new discord_js_1.Client({ + intents: [ + discord_js_1.GatewayIntentBits.Guilds, + discord_js_1.GatewayIntentBits.GuildMembers, + discord_js_1.GatewayIntentBits.GuildMessages, + discord_js_1.GatewayIntentBits.MessageContent, //Make sure this is enabled for text commands! + ], +}); +/** + * Where all of your dependencies are composed. + * '@sern/client' is usually your Discord Client. + * View documentation for pluggable dependencies + * Configure your dependency root to your liking. + * It follows the npm package iti https://itijs.org/. + * Use this function to access all of your dependencies. + * This is used for external event modules as well + */ +function init() { + return __awaiter(this, void 0, void 0, function* () { + yield (0, handler_1.makeDependencies)({ + build: (root) => root.add({ '@sern/client': (0, handler_1.single)(() => client) }) + }); + //View docs for all options + handler_1.Sern.init({ + defaultPrefix: '!', + commands: 'dist/commands', + // events: 'dist/events' (optional), + }); + }); +} +client.login(); diff --git a/tsconfig.json b/tsconfig.json index cdf5e10..9f6d830 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,7 @@ /* Modules */ "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ + "rootDir": "./src", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ @@ -55,7 +55,7 @@ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ @@ -105,5 +105,8 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } + }, + "include": [ + "./src" + ] } diff --git a/yarn.lock b/yarn.lock index e29924f..c47ea3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47,3 +47,8 @@ sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +typescript@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" + integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==