mirror of
https://github.com/discordjs/discord.js.git
synced 2024-08-21 22:14:43 +12:00
feat(create-discord-bot): bun/deno templates (#9795)
This commit is contained in:
parent
8eb978d32c
commit
dd5e7453e8
46 changed files with 700 additions and 115 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -23,6 +23,7 @@ dist-docs
|
||||||
.vscode
|
.vscode
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
|
!packages/create-discord-bot/template/**/.vscode
|
||||||
.idea
|
.idea
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.turbo
|
.turbo
|
||||||
|
@ -30,6 +31,12 @@ tsconfig.tsbuildinfo
|
||||||
coverage
|
coverage
|
||||||
out
|
out
|
||||||
|
|
||||||
|
# Deno
|
||||||
|
deno.lock
|
||||||
|
|
||||||
|
# Bun
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
# yarn
|
# yarn
|
||||||
.pnp.*
|
.pnp.*
|
||||||
.yarn/*
|
.yarn/*
|
||||||
|
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -14,5 +14,9 @@
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"npm.packageManager": "yarn",
|
"npm.packageManager": "yarn",
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"deno.enablePaths": ["./packages/create-discord-bot/template/Deno"],
|
||||||
|
"deno.lint": true,
|
||||||
|
"deno.unstable": false,
|
||||||
|
"deno.config": "./packages/create-discord-bot/template/Deno/deno.jsonc"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"extends": "../../.eslintrc.json"
|
"extends": "../../.eslintrc.json",
|
||||||
|
"ignorePatterns": ["**/template/Deno/*"]
|
||||||
}
|
}
|
||||||
|
|
2
packages/create-discord-bot/.gitignore
vendored
2
packages/create-discord-bot/.gitignore
vendored
|
@ -13,6 +13,8 @@ pids
|
||||||
|
|
||||||
# Env
|
# Env
|
||||||
.env
|
.env
|
||||||
|
!template/Bun/.env
|
||||||
|
!template/Deno/.env
|
||||||
!template/JavaScript/.env
|
!template/JavaScript/.env
|
||||||
!template/TypeScript/.env
|
!template/TypeScript/.env
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,9 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://discord.js.org",
|
"homepage": "https://discord.js.org",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"commander": "^11.0.0",
|
"commander": "^11.0.0",
|
||||||
|
"fast-glob": "^3.3.1",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
"prompts": "^2.4.2",
|
"prompts": "^2.4.2",
|
||||||
"validate-npm-package-name": "^5.0.0"
|
"validate-npm-package-name": "^5.0.0"
|
||||||
},
|
},
|
||||||
|
@ -62,6 +63,7 @@
|
||||||
"eslint-config-neon": "^0.1.47",
|
"eslint-config-neon": "^0.1.47",
|
||||||
"eslint-formatter-pretty": "^5.0.0",
|
"eslint-formatter-pretty": "^5.0.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
|
"terser": "^5.19.2",
|
||||||
"tsup": "^7.2.0",
|
"tsup": "^7.2.0",
|
||||||
"typescript": "^5.1.6",
|
"typescript": "^5.1.6",
|
||||||
"vitest": "^0.34.2"
|
"vitest": "^0.34.2"
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
|
import type { ExecException } from 'node:child_process';
|
||||||
import { cp, stat, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
import { cp, stat, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import chalk from 'chalk';
|
import glob from 'fast-glob';
|
||||||
import validateProjectName from 'validate-npm-package-name';
|
import { red, yellow, green, cyan } from 'picocolors';
|
||||||
import { install, resolvePackageManager } from './helpers/packageManager.js';
|
import type { PackageManager } from './helpers/packageManager.js';
|
||||||
|
import { install } from './helpers/packageManager.js';
|
||||||
import { GUIDE_URL } from './util/constants.js';
|
import { GUIDE_URL } from './util/constants.js';
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
directory: string;
|
directory: string;
|
||||||
javascript?: boolean;
|
packageManager: PackageManager;
|
||||||
typescript?: boolean;
|
typescript?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createDiscordBot({ typescript, javascript, directory }: Options) {
|
export async function createDiscordBot({ directory, typescript, packageManager }: Options) {
|
||||||
if (!directory) {
|
|
||||||
console.error(chalk.red('Please specify the project directory.'));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = path.resolve(directory);
|
const root = path.resolve(directory);
|
||||||
const directoryName = path.basename(root);
|
const directoryName = path.basename(root);
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
const directoryStats = await stat(root).catch(async (error) => {
|
const directoryStats = await stat(root).catch(async (error) => {
|
||||||
// Create a new directory if the specified one does not exist.
|
// Create a new directory if the specified one does not exist.
|
||||||
if (error.code === 'ENOENT') {
|
if (error.code === 'ENOENT') {
|
||||||
|
@ -34,47 +33,75 @@ export async function createDiscordBot({ typescript, javascript, directory }: Op
|
||||||
|
|
||||||
// If the directory is actually a file or if it's not empty, throw an error.
|
// If the directory is actually a file or if it's not empty, throw an error.
|
||||||
if (!directoryStats.isDirectory() || (await readdir(root)).length > 0) {
|
if (!directoryStats.isDirectory() || (await readdir(root)).length > 0) {
|
||||||
console.error(
|
console.error(red(`The directory ${yellow(`"${directoryName}"`)} is either not a directory or is not empty.`));
|
||||||
chalk.red(`The directory ${chalk.yellow(`"${directoryName}"`)} is either not a directory or is not empty.`),
|
console.error(red(`Please specify an empty directory.`));
|
||||||
);
|
|
||||||
console.error(chalk.red(`Please specify an empty directory.`));
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll use the directory name as the project name. Check npm name validity.
|
console.log(`Creating ${directoryName} in ${green(root)}.`);
|
||||||
const validationResult = validateProjectName(directoryName);
|
const deno = packageManager === 'deno';
|
||||||
|
await cp(new URL(`../template/${deno ? 'Deno' : typescript ? 'TypeScript' : 'JavaScript'}`, import.meta.url), root, {
|
||||||
if (!validationResult.validForNewPackages) {
|
|
||||||
console.error(
|
|
||||||
chalk.red(
|
|
||||||
`Cannot create a project named ${chalk.yellow(
|
|
||||||
`"${directoryName}"`,
|
|
||||||
)} due to npm naming restrictions.\n\nErrors:`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const error of [...(validationResult.errors ?? []), ...(validationResult.warnings ?? [])]) {
|
|
||||||
console.error(chalk.red(`- ${error}`));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(chalk.red('\nSee https://docs.npmjs.com/cli/configuring-npm/package-json for more details.'));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Creating ${directoryName} in ${chalk.green(root)}.`);
|
|
||||||
await cp(new URL(`../template/${typescript ? 'TypeScript' : 'JavaScript'}`, import.meta.url), root, {
|
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bun = packageManager === 'bun';
|
||||||
|
if (bun) {
|
||||||
|
await cp(
|
||||||
|
new URL(`../template/Bun/${typescript ? 'TypeScript' : 'JavaScript'}/package.json`, import.meta.url),
|
||||||
|
`${root}/package.json`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typescript) {
|
||||||
|
await cp(
|
||||||
|
new URL('../template/Bun/Typescript/tsconfig.eslint.json', import.meta.url),
|
||||||
|
`${root}/tsconfig.eslint.json`,
|
||||||
|
);
|
||||||
|
await cp(new URL('../template/Bun/Typescript/tsconfig.json', import.meta.url), `${root}/tsconfig.json`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
process.chdir(root);
|
process.chdir(root);
|
||||||
|
|
||||||
const newPackageJSON = await readFile('./package.json', { encoding: 'utf8' }).then((str) =>
|
const newVSCodeSettings = await readFile('./.vscode/settings.json', { encoding: 'utf8' }).then((str) => {
|
||||||
str.replace('[REPLACE-NAME]', directoryName),
|
let newStr = str.replace('[REPLACE_ME]', deno || bun ? 'auto' : packageManager);
|
||||||
);
|
if (deno) {
|
||||||
|
// @ts-expect-error: This is fine
|
||||||
|
newStr = newStr.replaceAll('"[REPLACE_BOOL]"', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newStr;
|
||||||
|
});
|
||||||
|
await writeFile('./.vscode/settings.json', newVSCodeSettings);
|
||||||
|
|
||||||
|
const globStream = glob.stream('./src/**/*.ts');
|
||||||
|
for await (const file of globStream) {
|
||||||
|
const newData = await readFile(file, { encoding: 'utf8' }).then((str) =>
|
||||||
|
str.replaceAll('[REPLACE_IMPORT_EXT]', typescript ? 'ts' : 'js'),
|
||||||
|
);
|
||||||
|
await writeFile(file, newData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPackageJSON = await readFile('./package.json', { encoding: 'utf8' }).then((str) => {
|
||||||
|
let newStr = str.replace('[REPLACE_ME]', directoryName);
|
||||||
|
newStr = newStr.replaceAll('[REPLACE_IMPORT_EXT]', typescript ? 'ts' : 'js');
|
||||||
|
return newStr;
|
||||||
|
});
|
||||||
await writeFile('./package.json', newPackageJSON);
|
await writeFile('./package.json', newPackageJSON);
|
||||||
|
|
||||||
const packageManager = resolvePackageManager();
|
try {
|
||||||
install(packageManager);
|
install(packageManager);
|
||||||
console.log(chalk.green('All done! Be sure to read through the discord.js guide for help on your journey.'));
|
} catch (error) {
|
||||||
console.log(`Link: ${chalk.cyan(GUIDE_URL)}`);
|
console.log();
|
||||||
|
const err = error as ExecException;
|
||||||
|
if (err.signal === 'SIGINT') {
|
||||||
|
console.log(red('Installation aborted.'));
|
||||||
|
} else {
|
||||||
|
console.error(red('Installation failed.'));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
console.log(green('All done! Be sure to read through the discord.js guide for help on your journey.'));
|
||||||
|
console.log(`Link: ${cyan(GUIDE_URL)}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { execSync } from 'node:child_process';
|
import { execSync } from 'node:child_process';
|
||||||
import process from 'node:process';
|
import process from 'node:process';
|
||||||
import chalk from 'chalk';
|
import { yellow } from 'picocolors';
|
||||||
import { DEFAULT_PACKAGE_MANAGER } from '../util/constants.js';
|
import { DEFAULT_PACKAGE_MANAGER } from '../util/constants.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A union of supported package managers.
|
* A union of supported package managers.
|
||||||
*/
|
*/
|
||||||
export type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
export type PackageManager = 'bun' | 'deno' | 'npm' | 'pnpm' | 'yarn';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the package manager from `npm_config_user_agent`.
|
* Resolves the package manager from `npm_config_user_agent`.
|
||||||
|
@ -32,7 +32,7 @@ export function resolvePackageManager(): PackageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error(
|
console.error(
|
||||||
chalk.yellow(
|
yellow(
|
||||||
`Detected an unsupported package manager (${npmConfigUserAgent}). Falling back to ${DEFAULT_PACKAGE_MANAGER}.`,
|
`Detected an unsupported package manager (${npmConfigUserAgent}). Falling back to ${DEFAULT_PACKAGE_MANAGER}.`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -47,21 +47,62 @@ export function resolvePackageManager(): PackageManager {
|
||||||
* @param packageManager - The package manager to use
|
* @param packageManager - The package manager to use
|
||||||
*/
|
*/
|
||||||
export function install(packageManager: PackageManager) {
|
export function install(packageManager: PackageManager) {
|
||||||
let installCommand;
|
let installCommand: string[] | string = `${packageManager} install`;
|
||||||
|
|
||||||
switch (packageManager) {
|
|
||||||
case 'npm':
|
|
||||||
case 'pnpm':
|
|
||||||
installCommand = `${packageManager} install`;
|
|
||||||
break;
|
|
||||||
case 'yarn':
|
|
||||||
installCommand = packageManager;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Installing dependencies with ${packageManager}...`);
|
console.log(`Installing dependencies with ${packageManager}...`);
|
||||||
|
|
||||||
|
switch (packageManager) {
|
||||||
|
case 'yarn':
|
||||||
|
console.log();
|
||||||
|
installCommand = [
|
||||||
|
`${packageManager} set version stable`,
|
||||||
|
`${packageManager} config set nodeLinker node-modules`,
|
||||||
|
`${packageManager} config set logFilters --json '[{ "code": "YN0002", "level": "discard" }, { "code": "YN0013", "level": "discard" }, { "code": "YN0032", "level": "discard" }, { "code": "YN0060", "level": "discard" }]'`,
|
||||||
|
`${packageManager} plugin import interactive-tools`,
|
||||||
|
`${packageManager} plugin import workspace-tools`,
|
||||||
|
installCommand,
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
case 'deno':
|
||||||
|
installCommand = `${packageManager} cache --reload src/index.ts`;
|
||||||
|
break;
|
||||||
|
case 'pnpm':
|
||||||
|
case 'bun':
|
||||||
|
console.log();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
...process.env,
|
||||||
|
ADBLOCK: '1',
|
||||||
|
NODE_ENV: 'development',
|
||||||
|
DISABLE_OPENCOLLECTIVE: '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Array.isArray(installCommand)) {
|
||||||
|
for (const [index, command] of installCommand.entries()) {
|
||||||
|
if (index === installCommand.length - 1) {
|
||||||
|
execSync(command, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
env,
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
execSync(command, {
|
||||||
|
stdio: 'ignore',
|
||||||
|
env,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
execSync(installCommand, {
|
execSync(installCommand, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
|
env,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,94 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// eslint-disable-next-line n/shebang
|
// eslint-disable-next-line n/shebang
|
||||||
import { program } from 'commander';
|
import process from 'node:process';
|
||||||
|
import { Option, program } from 'commander';
|
||||||
|
import { red, yellow, green } from 'picocolors';
|
||||||
import prompts from 'prompts';
|
import prompts from 'prompts';
|
||||||
|
import validateProjectName from 'validate-npm-package-name';
|
||||||
|
import packageJSON from '../package.json' assert { type: 'json' };
|
||||||
import { createDiscordBot } from './create-discord-bot.js';
|
import { createDiscordBot } from './create-discord-bot.js';
|
||||||
|
import { resolvePackageManager } from './helpers/packageManager.js';
|
||||||
|
import { DEFAULT_PROJECT_NAME, PACKAGE_MANAGERS } from './util/constants.js';
|
||||||
|
|
||||||
|
let projectDirectory = '';
|
||||||
|
|
||||||
|
const handleSigTerm = () => process.exit(0);
|
||||||
|
|
||||||
|
process.on('SIGINT', handleSigTerm);
|
||||||
|
process.on('SIGTERM', handleSigTerm);
|
||||||
|
|
||||||
|
// https://github.com/vercel/next.js/blob/canary/packages/create-next-app/index.ts#L24-L32
|
||||||
|
const onPromptState = (state: any) => {
|
||||||
|
if (state.aborted) {
|
||||||
|
// If we don't re-enable the terminal cursor before exiting
|
||||||
|
// the program, the cursor will remain hidden
|
||||||
|
process.stdout.write('\u001B[?25h');
|
||||||
|
process.stdout.write('\n');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
program
|
program
|
||||||
|
.name(packageJSON.name)
|
||||||
|
.version(packageJSON.version)
|
||||||
.description('Create a basic discord.js bot.')
|
.description('Create a basic discord.js bot.')
|
||||||
.option('--directory', 'The directory where this will be created.')
|
.argument('[directory]', 'What is the name of the directory you want to create this project in?')
|
||||||
|
.usage(`${green('<directory>')}`)
|
||||||
|
.action((directory) => {
|
||||||
|
projectDirectory = directory;
|
||||||
|
})
|
||||||
.option('--typescript', 'Whether to use the TypeScript template.')
|
.option('--typescript', 'Whether to use the TypeScript template.')
|
||||||
.option('--javascript', 'Whether to use the JavaScript template.')
|
.option('--javascript', 'Whether to use the JavaScript template.')
|
||||||
|
.addOption(
|
||||||
|
new Option('--packageManager <packageManager>', 'The package manager to use.')
|
||||||
|
.choices(PACKAGE_MANAGERS)
|
||||||
|
.default(resolvePackageManager()),
|
||||||
|
)
|
||||||
|
.allowUnknownOption()
|
||||||
.parse();
|
.parse();
|
||||||
|
|
||||||
let { typescript, javascript, directory } = program.opts();
|
// eslint-disable-next-line prefer-const
|
||||||
|
let { typescript, javascript, packageManager } = program.opts();
|
||||||
|
|
||||||
if (!directory) {
|
if (!projectDirectory) {
|
||||||
directory = (
|
projectDirectory = (
|
||||||
await prompts({
|
await prompts({
|
||||||
|
onState: onPromptState,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'directory',
|
name: 'directory',
|
||||||
initial: 'my-bot',
|
initial: DEFAULT_PROJECT_NAME,
|
||||||
message: 'What is the name of the directory you want to create this project in?',
|
message: 'What is the name of the directory you want to create this project in?',
|
||||||
|
validate: (directory) => {
|
||||||
|
// We'll use the directory name as the project name. Check npm name validity.
|
||||||
|
const validationResult = validateProjectName(directory);
|
||||||
|
|
||||||
|
if (!validationResult.validForNewPackages) {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
for (const error of [...(validationResult.errors ?? []), ...(validationResult.warnings ?? [])]) {
|
||||||
|
errors.push(red(`- ${error}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return red(
|
||||||
|
`Cannot create a project named ${yellow(
|
||||||
|
`"${directory}"`,
|
||||||
|
)} due to npm naming restrictions.\n\nErrors:\n${errors.join('\n')}\n\n${red(
|
||||||
|
'\nSee https://docs.npmjs.com/cli/configuring-npm/package-json for more details.',
|
||||||
|
)}}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
})
|
})
|
||||||
).directory;
|
).directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typescript === undefined && javascript === undefined) {
|
const deno = packageManager === 'deno';
|
||||||
|
if (!deno && typescript === undefined && javascript === undefined) {
|
||||||
const { useTypescript } = await prompts({
|
const { useTypescript } = await prompts({
|
||||||
|
onState: onPromptState,
|
||||||
type: 'toggle',
|
type: 'toggle',
|
||||||
name: 'useTypescript',
|
name: 'useTypescript',
|
||||||
message: 'Do you want to use TypeScript?',
|
message: 'Do you want to use TypeScript?',
|
||||||
|
@ -36,7 +98,6 @@ if (typescript === undefined && javascript === undefined) {
|
||||||
});
|
});
|
||||||
|
|
||||||
typescript = useTypescript;
|
typescript = useTypescript;
|
||||||
javascript = !useTypescript;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await createDiscordBot({ typescript, javascript, directory });
|
await createDiscordBot({ typescript, directory: projectDirectory, packageManager });
|
||||||
|
|
|
@ -3,6 +3,16 @@
|
||||||
*/
|
*/
|
||||||
export const DEFAULT_PACKAGE_MANAGER = 'npm' as const;
|
export const DEFAULT_PACKAGE_MANAGER = 'npm' as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default project name.
|
||||||
|
*/
|
||||||
|
export const DEFAULT_PROJECT_NAME = 'my-bot' as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The supported package managers.
|
||||||
|
*/
|
||||||
|
export const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun', 'deno'] as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL to the guide.
|
* The URL to the guide.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "[REPLACE_ME]",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "prettier --check . && eslint ./src --ext .[REPLACE_IMPORT_EXT] --format=pretty",
|
||||||
|
"deploy": "bun run src/util/deploy.[REPLACE_IMPORT_EXT]",
|
||||||
|
"format": "prettier --write . && eslint ./src --ext .[REPLACE_IMPORT_EXT] --fix --format=pretty",
|
||||||
|
"start": "bun run src/index.[REPLACE_IMPORT_EXT]"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@discordjs/core": "^1.0.1",
|
||||||
|
"discord.js": "^14.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sapphire/ts-config": "^4.0.1",
|
||||||
|
"eslint": "^8.47.0",
|
||||||
|
"eslint-config-neon": "^0.1.47",
|
||||||
|
"eslint-formatter-pretty": "^5.0.0",
|
||||||
|
"prettier": "^3.0.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "[REPLACE_ME]",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"lint": "tsc && prettier --check . && eslint ./src --ext .[REPLACE_IMPORT_EXT] --format=pretty",
|
||||||
|
"deploy": "bun run src/util/deploy.[REPLACE_IMPORT_EXT]",
|
||||||
|
"format": "prettier --write . && eslint ./src --ext .[REPLACE_IMPORT_EXT] --fix --format=pretty",
|
||||||
|
"start": "bun run src/index.[REPLACE_IMPORT_EXT]"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@discordjs/core": "^1.0.1",
|
||||||
|
"discord.js": "^14.13.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sapphire/ts-config": "^4.0.1",
|
||||||
|
"bun-types": "^0.7.3",
|
||||||
|
"eslint": "^8.47.0",
|
||||||
|
"eslint-config-neon": "^0.1.47",
|
||||||
|
"eslint-formatter-pretty": "^5.0.0",
|
||||||
|
"prettier": "^3.0.2",
|
||||||
|
"typescript": "^5.1.6"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "**/*.js", "**/*.test.ts", "**/*.test.js"],
|
||||||
|
"exclude": []
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"extends": "@sapphire/ts-config/extra-strict",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": false,
|
||||||
|
"declarationMap": false,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"target": "ESNext",
|
||||||
|
"outDir": "dist",
|
||||||
|
"noEmit": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"types": ["bun-types"],
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
2
packages/create-discord-bot/template/Deno/.env
Normal file
2
packages/create-discord-bot/template/Deno/.env
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
DISCORD_TOKEN=
|
||||||
|
APPLICATION_ID=
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"useTabs": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"quoteProps": "as-needed",
|
||||||
|
"trailingComma": "all",
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
3
packages/create-discord-bot/template/Deno/.vscode/extensions.json
vendored
Normal file
3
packages/create-discord-bot/template/Deno/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"recommendations": ["denoland.vscode-deno", "tamasfe.even-better-toml", "codezombiech.gitignore"]
|
||||||
|
}
|
15
packages/create-discord-bot/template/Deno/.vscode/settings.json
vendored
Normal file
15
packages/create-discord-bot/template/Deno/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"editor.defaultFormatter": "denoland.vscode-deno",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": true,
|
||||||
|
"source.organizeImports": false
|
||||||
|
},
|
||||||
|
"editor.trimAutoWhitespace": false,
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.eol": "\n",
|
||||||
|
"npm.packageManager": "[REPLACE_ME]",
|
||||||
|
"deno.enable": "[REPLACE_BOOL]",
|
||||||
|
"deno.lint": "[REPLACE_BOOL]",
|
||||||
|
"deno.unstable": false
|
||||||
|
}
|
40
packages/create-discord-bot/template/Deno/deno.jsonc
Normal file
40
packages/create-discord-bot/template/Deno/deno.jsonc
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
|
||||||
|
"tasks": {
|
||||||
|
"lint": "deno lint",
|
||||||
|
"deploy": "deno run --allow-read --allow-env --allow-net src/util/deploy.ts",
|
||||||
|
"format": "deno fmt",
|
||||||
|
"start": "deno run --allow-read --allow-env --allow-net src/index.ts"
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"include": ["src/"],
|
||||||
|
"rules": {
|
||||||
|
"tags": ["recommended"],
|
||||||
|
"exclude": ["require-await", "no-await-in-sync-fn"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fmt": {
|
||||||
|
"useTabs": true,
|
||||||
|
"lineWidth": 120,
|
||||||
|
"semiColons": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"proseWrap": "preserve",
|
||||||
|
"include": ["src/"]
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"lib": ["deno.window"],
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"removeComments": false,
|
||||||
|
"strict": true,
|
||||||
|
"allowUnreachableCode": false,
|
||||||
|
"allowUnusedLabels": false,
|
||||||
|
"exactOptionalPropertyTypes": false,
|
||||||
|
"noImplicitOverride": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'npm:discord.js@^14.13.0';
|
||||||
|
import type { StructurePredicate } from '../util/loaders.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the structure of a command
|
||||||
|
*/
|
||||||
|
export type Command = {
|
||||||
|
/**
|
||||||
|
* The data for the command
|
||||||
|
*/
|
||||||
|
data: RESTPostAPIApplicationCommandsJSONBody;
|
||||||
|
/**
|
||||||
|
* The function to execute when the command is called
|
||||||
|
*
|
||||||
|
* @param interaction - The interaction of the command
|
||||||
|
*/
|
||||||
|
execute(interaction: CommandInteraction): Promise<void> | void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defines the predicate to check if an object is a valid Command type
|
||||||
|
export const predicate: StructurePredicate<Command> = (structure): structure is Command =>
|
||||||
|
Boolean(structure) &&
|
||||||
|
typeof structure === 'object' &&
|
||||||
|
'data' in structure! &&
|
||||||
|
'execute' in structure &&
|
||||||
|
typeof structure.data === 'object' &&
|
||||||
|
typeof structure.execute === 'function';
|
|
@ -0,0 +1,11 @@
|
||||||
|
import type { Command } from './index.ts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data: {
|
||||||
|
name: 'ping',
|
||||||
|
description: 'Ping!',
|
||||||
|
},
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.reply('Pong!');
|
||||||
|
},
|
||||||
|
} satisfies Command;
|
|
@ -0,0 +1,33 @@
|
||||||
|
import type { ClientEvents } from 'npm:discord.js@^14.13.0';
|
||||||
|
import type { StructurePredicate } from '../util/loaders.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the structure of an event.
|
||||||
|
*/
|
||||||
|
export type Event<T extends keyof ClientEvents = keyof ClientEvents> = {
|
||||||
|
/**
|
||||||
|
* The function to execute when the event is emitted.
|
||||||
|
*
|
||||||
|
* @param parameters - The parameters of the event
|
||||||
|
*/
|
||||||
|
execute(...parameters: ClientEvents[T]): Promise<void> | void;
|
||||||
|
/**
|
||||||
|
* The name of the event to listen to
|
||||||
|
*/
|
||||||
|
name: T;
|
||||||
|
/**
|
||||||
|
* Whether or not the event should only be listened to once
|
||||||
|
*
|
||||||
|
* @defaultValue false
|
||||||
|
*/
|
||||||
|
once?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defines the predicate to check if an object is a valid Event type.
|
||||||
|
export const predicate: StructurePredicate<Event> = (structure): structure is Event =>
|
||||||
|
Boolean(structure) &&
|
||||||
|
typeof structure === 'object' &&
|
||||||
|
'name' in structure! &&
|
||||||
|
'execute' in structure &&
|
||||||
|
typeof structure.name === 'string' &&
|
||||||
|
typeof structure.execute === 'function';
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Events } from 'npm:discord.js@^14.13.0';
|
||||||
|
import type { Event } from './index.ts';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: Events.ClientReady,
|
||||||
|
once: true,
|
||||||
|
async execute(client) {
|
||||||
|
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||||
|
},
|
||||||
|
} satisfies Event<Events.ClientReady>;
|
18
packages/create-discord-bot/template/Deno/src/index.ts
Normal file
18
packages/create-discord-bot/template/Deno/src/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import 'https://deno.land/std@0.199.0/dotenv/load.ts';
|
||||||
|
import { URL } from 'node:url';
|
||||||
|
import { Client, GatewayIntentBits } from 'npm:discord.js@^14.13.0';
|
||||||
|
import { loadCommands, loadEvents } from './util/loaders.ts';
|
||||||
|
import { registerEvents } from './util/registerEvents.ts';
|
||||||
|
|
||||||
|
// Initialize the client
|
||||||
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||||
|
|
||||||
|
// Load the events and commands
|
||||||
|
const events = await loadEvents(new URL('events/', import.meta.url));
|
||||||
|
const commands = await loadCommands(new URL('commands/', import.meta.url));
|
||||||
|
|
||||||
|
// Register the event handlers
|
||||||
|
registerEvents(commands, events, client);
|
||||||
|
|
||||||
|
// Login to the client
|
||||||
|
void client.login(Deno.env.get('DISCORD_TOKEN'));
|
15
packages/create-discord-bot/template/Deno/src/util/deploy.ts
Normal file
15
packages/create-discord-bot/template/Deno/src/util/deploy.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import 'https://deno.land/std@0.199.0/dotenv/load.ts';
|
||||||
|
import { URL } from 'node:url';
|
||||||
|
import { API } from 'npm:@discordjs/core@^1.0.1/http-only';
|
||||||
|
import { REST } from 'npm:discord.js@^14.13.0';
|
||||||
|
import { loadCommands } from './loaders.ts';
|
||||||
|
|
||||||
|
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
||||||
|
const commandData = [...commands.values()].map((command) => command.data);
|
||||||
|
|
||||||
|
const rest = new REST({ version: '10' }).setToken(Deno.env.get('DISCORD_TOKEN')!);
|
||||||
|
const api = new API(rest);
|
||||||
|
|
||||||
|
const result = await api.applicationCommands.bulkOverwriteGlobalCommands(Deno.env.get('APPLICATION_ID')!, commandData);
|
||||||
|
|
||||||
|
console.log(`Successfully registered ${result.length} commands.`);
|
|
@ -0,0 +1,76 @@
|
||||||
|
import type { PathLike } from 'node:fs';
|
||||||
|
import { readdir, stat } from 'node:fs/promises';
|
||||||
|
import { URL } from 'node:url';
|
||||||
|
import type { Command } from '../commands/index.ts';
|
||||||
|
import { predicate as commandPredicate } from '../commands/index.ts';
|
||||||
|
import type { Event } from '../events/index.ts';
|
||||||
|
import { predicate as eventPredicate } from '../events/index.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A predicate to check if the structure is valid
|
||||||
|
*/
|
||||||
|
export type StructurePredicate<T> = (structure: unknown) => structure is T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all the structures in the provided directory
|
||||||
|
*
|
||||||
|
* @param dir - The directory to load the structures from
|
||||||
|
* @param predicate - The predicate to check if the structure is valid
|
||||||
|
* @param recursive - Whether to recursively load the structures in the directory
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function loadStructures<T>(
|
||||||
|
dir: PathLike,
|
||||||
|
predicate: StructurePredicate<T>,
|
||||||
|
recursive = true,
|
||||||
|
): Promise<T[]> {
|
||||||
|
// Get the stats of the directory
|
||||||
|
const statDir = await stat(dir);
|
||||||
|
|
||||||
|
// If the provided directory path is not a directory, throw an error
|
||||||
|
if (!statDir.isDirectory()) {
|
||||||
|
throw new Error(`The directory '${dir}' is not a directory.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the files in the directory
|
||||||
|
const files = await readdir(dir);
|
||||||
|
|
||||||
|
// Create an empty array to store the structures
|
||||||
|
const structures: T[] = [];
|
||||||
|
|
||||||
|
// Loop through all the files in the directory
|
||||||
|
for (const file of files) {
|
||||||
|
// If the file is index.js or the file does not end with .js, skip the file
|
||||||
|
if (file === 'index.ts' || !file.endsWith('.ts')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the stats of the file
|
||||||
|
const statFile = await stat(new URL(`${dir}/${file}`));
|
||||||
|
|
||||||
|
// If the file is a directory and recursive is true, recursively load the structures in the directory
|
||||||
|
if (statFile.isDirectory() && recursive) {
|
||||||
|
structures.push(...(await loadStructures(`${dir}/${file}`, predicate, recursive)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import the structure dynamically from the file
|
||||||
|
const structure = (await import(`${dir}/${file}`)).default;
|
||||||
|
|
||||||
|
// If the structure is a valid structure, add it
|
||||||
|
if (predicate(structure)) structures.push(structure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return structures;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadCommands(dir: PathLike, recursive = true): Promise<Map<string, Command>> {
|
||||||
|
return (await loadStructures(dir, commandPredicate, recursive)).reduce(
|
||||||
|
(acc, cur) => acc.set(cur.data.name, cur),
|
||||||
|
new Map<string, Command>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadEvents(dir: PathLike, recursive = true): Promise<Event[]> {
|
||||||
|
return loadStructures(dir, eventPredicate, recursive);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Events, type Client } from 'npm:discord.js@^14.13.0';
|
||||||
|
import type { Command } from '../commands/index.ts';
|
||||||
|
import type { Event } from '../events/index.ts';
|
||||||
|
|
||||||
|
export function registerEvents(commands: Map<string, Command>, events: Event[], client: Client): void {
|
||||||
|
// Create an event to handle command interactions
|
||||||
|
const interactionCreateEvent: Event<Events.InteractionCreate> = {
|
||||||
|
name: Events.InteractionCreate,
|
||||||
|
async execute(interaction) {
|
||||||
|
if (interaction.isCommand()) {
|
||||||
|
const command = commands.get(interaction.commandName);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
throw new Error(`Command '${interaction.commandName}' not found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await command.execute(interaction);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const event of [...events, interactionCreateEvent]) {
|
||||||
|
client[event.once ? 'once' : 'on'](event.name, async (...args) => event.execute(...args));
|
||||||
|
}
|
||||||
|
}
|
10
packages/create-discord-bot/template/JavaScript/.vscode/extensions.json
vendored
Normal file
10
packages/create-discord-bot/template/JavaScript/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"tamasfe.even-better-toml",
|
||||||
|
"codezombiech.gitignore",
|
||||||
|
"christian-kohler.npm-intellisense",
|
||||||
|
"christian-kohler.path-intellisense"
|
||||||
|
]
|
||||||
|
}
|
13
packages/create-discord-bot/template/JavaScript/.vscode/settings.json
vendored
Normal file
13
packages/create-discord-bot/template/JavaScript/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": true,
|
||||||
|
"source.organizeImports": false
|
||||||
|
},
|
||||||
|
"editor.trimAutoWhitespace": false,
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.eol": "\n",
|
||||||
|
"npm.packageManager": "[REPLACE_ME]"
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "[REPLACE-NAME]",
|
"name": "[REPLACE_ME]",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@ -10,14 +10,14 @@
|
||||||
"deploy": "node --require dotenv/config src/util/deploy.js"
|
"deploy": "node --require dotenv/config src/util/deploy.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/core": "^0.6.0",
|
"@discordjs/core": "^1.0.1",
|
||||||
"discord.js": "^14.11.0",
|
"discord.js": "^14.13.0",
|
||||||
"dotenv": "^16.0.3"
|
"dotenv": "^16.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.47.0",
|
||||||
"eslint-config-neon": "^0.1.47",
|
"eslint-config-neon": "^0.1.47",
|
||||||
"eslint-formatter-pretty": "^5.0.0",
|
"eslint-formatter-pretty": "^5.0.0",
|
||||||
"prettier": "^2.8.8"
|
"prettier": "^3.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import process from 'node:process';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { Client, GatewayIntentBits } from 'discord.js';
|
import { Client, GatewayIntentBits } from 'discord.js';
|
||||||
import { loadCommands, loadEvents } from './util/loaders.js';
|
import { loadCommands, loadEvents } from './util/loaders.js';
|
||||||
|
@ -14,4 +15,4 @@ const commands = await loadCommands(new URL('commands/', import.meta.url));
|
||||||
registerEvents(commands, events, client);
|
registerEvents(commands, events, client);
|
||||||
|
|
||||||
// Login to the client
|
// Login to the client
|
||||||
void client.login();
|
void client.login(process.env.DISCORD_TOKEN);
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { API } from '@discordjs/core/http-only';
|
||||||
import { REST } from 'discord.js';
|
import { REST } from 'discord.js';
|
||||||
import { loadCommands } from './loaders.js';
|
import { loadCommands } from './loaders.js';
|
||||||
|
|
||||||
const commands = await loadCommands(new URL('commands/', import.meta.url));
|
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
||||||
const commandData = [...commands.values()].map((command) => command.data);
|
const commandData = [...commands.values()].map((command) => command.data);
|
||||||
|
|
||||||
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);
|
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
|
||||||
const api = new API(rest);
|
const api = new API(rest);
|
||||||
|
|
||||||
const result = await api.applicationCommands.bulkOverwriteGlobalCommands(process.env.APPLICATION_ID, commandData);
|
const result = await api.applicationCommands.bulkOverwriteGlobalCommands(process.env.APPLICATION_ID, commandData);
|
||||||
|
|
10
packages/create-discord-bot/template/TypeScript/.vscode/extensions.json
vendored
Normal file
10
packages/create-discord-bot/template/TypeScript/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"tamasfe.even-better-toml",
|
||||||
|
"codezombiech.gitignore",
|
||||||
|
"christian-kohler.npm-intellisense",
|
||||||
|
"christian-kohler.path-intellisense"
|
||||||
|
]
|
||||||
|
}
|
13
packages/create-discord-bot/template/TypeScript/.vscode/settings.json
vendored
Normal file
13
packages/create-discord-bot/template/TypeScript/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": true,
|
||||||
|
"source.organizeImports": false
|
||||||
|
},
|
||||||
|
"editor.trimAutoWhitespace": false,
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.eol": "\n",
|
||||||
|
"npm.packageManager": "[REPLACE_ME]"
|
||||||
|
}
|
|
@ -1,27 +1,27 @@
|
||||||
{
|
{
|
||||||
"name": "[REPLACE-NAME]",
|
"name": "[REPLACE_ME]",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "tsc",
|
"build": "tsc",
|
||||||
"lint": "prettier --check . && eslint ./src --ext .ts --format=pretty",
|
"lint": "prettier --check . && eslint ./src --ext .ts --format=pretty",
|
||||||
"deploy": "node --require dotenv/config dist/util/deploy.js",
|
"deploy": "node --require dotenv/config dist/util/deploy.js",
|
||||||
"format": "prettier --write . && eslint ./src --ext .ts --fix --format=pretty",
|
"format": "prettier --write . && eslint ./src --ext .ts --fix --format=pretty",
|
||||||
"start": "node --require dotenv/config dist/index.js"
|
"start": "node --require dotenv/config dist/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/core": "^0.6.0",
|
"@discordjs/core": "^1.0.1",
|
||||||
"discord.js": "^14.11.0",
|
"discord.js": "^14.13.0",
|
||||||
"dotenv": "^16.0.3"
|
"dotenv": "^16.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sapphire/ts-config": "^4.0.0",
|
"@sapphire/ts-config": "^4.0.1",
|
||||||
"@types/node": "^18.15.3",
|
"@types/node": "^18.17.6",
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.47.0",
|
||||||
"eslint-config-neon": "^0.1.47",
|
"eslint-config-neon": "^0.1.47",
|
||||||
"eslint-formatter-pretty": "^5.0.0",
|
"eslint-formatter-pretty": "^5.0.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.0.2",
|
||||||
"typescript": "^5.0.4"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'discord.js';
|
import type { RESTPostAPIApplicationCommandsJSONBody, CommandInteraction } from 'discord.js';
|
||||||
import type { StructurePredicate } from '../util/loaders.js';
|
import type { StructurePredicate } from '../util/loaders.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the structure of a command
|
* Defines the structure of a command
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Command } from './index.js';
|
import type { Command } from './index.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ClientEvents } from 'discord.js';
|
import type { ClientEvents } from 'discord.js';
|
||||||
import type { StructurePredicate } from '../util/loaders.js';
|
import type { StructurePredicate } from '../util/loaders.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the structure of an event.
|
* Defines the structure of an event.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Events } from 'discord.js';
|
import { Events } from 'discord.js';
|
||||||
import type { Event } from './index.js';
|
import type { Event } from './index.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: Events.ClientReady,
|
name: Events.ClientReady,
|
||||||
|
@ -7,4 +7,4 @@ export default {
|
||||||
async execute(client) {
|
async execute(client) {
|
||||||
console.log(`Ready! Logged in as ${client.user.tag}`);
|
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||||
},
|
},
|
||||||
} satisfies Event<'ready'>;
|
} satisfies Event<Events.ClientReady>;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import process from 'node:process';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { Client, GatewayIntentBits } from 'discord.js';
|
import { Client, GatewayIntentBits } from 'discord.js';
|
||||||
import { loadCommands, loadEvents } from './util/loaders.js';
|
import { loadCommands, loadEvents } from './util/loaders.[REPLACE_IMPORT_EXT]';
|
||||||
import { registerEvents } from './util/registerEvents.js';
|
import { registerEvents } from './util/registerEvents.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
// Initialize the client
|
// Initialize the client
|
||||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||||
|
@ -14,4 +15,4 @@ const commands = await loadCommands(new URL('commands/', import.meta.url));
|
||||||
registerEvents(commands, events, client);
|
registerEvents(commands, events, client);
|
||||||
|
|
||||||
// Login to the client
|
// Login to the client
|
||||||
void client.login();
|
void client.login(process.env.DISCORD_TOKEN);
|
||||||
|
|
|
@ -2,12 +2,12 @@ import process from 'node:process';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import { API } from '@discordjs/core/http-only';
|
import { API } from '@discordjs/core/http-only';
|
||||||
import { REST } from 'discord.js';
|
import { REST } from 'discord.js';
|
||||||
import { loadCommands } from './loaders.js';
|
import { loadCommands } from './loaders.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
const commands = await loadCommands(new URL('commands/', import.meta.url));
|
const commands = await loadCommands(new URL('../commands/', import.meta.url));
|
||||||
const commandData = [...commands.values()].map((command) => command.data);
|
const commandData = [...commands.values()].map((command) => command.data);
|
||||||
|
|
||||||
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN!);
|
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN!);
|
||||||
const api = new API(rest);
|
const api = new API(rest);
|
||||||
|
|
||||||
const result = await api.applicationCommands.bulkOverwriteGlobalCommands(process.env.APPLICATION_ID!, commandData);
|
const result = await api.applicationCommands.bulkOverwriteGlobalCommands(process.env.APPLICATION_ID!, commandData);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import type { PathLike } from 'node:fs';
|
import type { PathLike } from 'node:fs';
|
||||||
import { readdir, stat } from 'node:fs/promises';
|
import { readdir, stat } from 'node:fs/promises';
|
||||||
import { URL } from 'node:url';
|
import { URL } from 'node:url';
|
||||||
import type { Command } from '../commands/index.js';
|
import type { Command } from '../commands/index.[REPLACE_IMPORT_EXT]';
|
||||||
import { predicate as commandPredicate } from '../commands/index.js';
|
import { predicate as commandPredicate } from '../commands/index.[REPLACE_IMPORT_EXT]';
|
||||||
import type { Event } from '../events/index.js';
|
import type { Event } from '../events/index.[REPLACE_IMPORT_EXT]';
|
||||||
import { predicate as eventPredicate } from '../events/index.js';
|
import { predicate as eventPredicate } from '../events/index.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A predicate to check if the structure is valid
|
* A predicate to check if the structure is valid
|
||||||
|
@ -40,8 +40,8 @@ export async function loadStructures<T>(
|
||||||
|
|
||||||
// Loop through all the files in the directory
|
// Loop through all the files in the directory
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
// If the file is index.js or the file does not end with .js, skip the file
|
// If the file is index.[REPLACE_IMPORT_EXT] or the file does not end with .[REPLACE_IMPORT_EXT], skip the file
|
||||||
if (file === 'index.js' || !file.endsWith('.js')) {
|
if (file === 'index.[REPLACE_IMPORT_EXT]' || !file.endsWith('.[REPLACE_IMPORT_EXT]')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Events, type Client } from 'discord.js';
|
import { Events, type Client } from 'discord.js';
|
||||||
import type { Command } from '../commands/index.js';
|
import type { Command } from '../commands/index.[REPLACE_IMPORT_EXT]';
|
||||||
import type { Event } from '../events/index.js';
|
import type { Event } from '../events/index.[REPLACE_IMPORT_EXT]';
|
||||||
|
|
||||||
export function registerEvents(commands: Map<string, Command>, events: Event[], client: Client): void {
|
export function registerEvents(commands: Map<string, Command>, events: Event[], client: Client): void {
|
||||||
// Create an event to handle command interactions
|
// Create an event to handle command interactions
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"outDir": "dist"
|
"outDir": "dist",
|
||||||
|
"skipLibCheck": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,5 @@
|
||||||
"**/*.spec.js",
|
"**/*.spec.js",
|
||||||
"**/*.spec.mjs"
|
"**/*.spec.mjs"
|
||||||
],
|
],
|
||||||
"exclude": []
|
"exclude": ["template/Deno"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ export default createTsupConfig({
|
||||||
entry: ['src/index.ts'],
|
entry: ['src/index.ts'],
|
||||||
dts: false,
|
dts: false,
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
minify: true,
|
minify: 'terser',
|
||||||
|
keepNames: false,
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
});
|
});
|
||||||
|
|
36
yarn.lock
36
yarn.lock
|
@ -3624,6 +3624,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@jridgewell/source-map@npm:^0.3.3":
|
||||||
|
version: 0.3.5
|
||||||
|
resolution: "@jridgewell/source-map@npm:0.3.5"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping": ^0.3.0
|
||||||
|
"@jridgewell/trace-mapping": ^0.3.9
|
||||||
|
checksum: 1ad4dec0bdafbade57920a50acec6634f88a0eb735851e0dda906fa9894e7f0549c492678aad1a10f8e144bfe87f238307bf2a914a1bc85b7781d345417e9f6f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15":
|
"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15":
|
||||||
version: 1.4.15
|
version: 1.4.15
|
||||||
resolution: "@jridgewell/sourcemap-codec@npm:1.4.15"
|
resolution: "@jridgewell/sourcemap-codec@npm:1.4.15"
|
||||||
|
@ -8214,7 +8224,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.10.0, acorn@npm:^8.4.1, acorn@npm:^8.6.0, acorn@npm:^8.9.0":
|
"acorn@npm:^8.0.0, acorn@npm:^8.0.4, acorn@npm:^8.10.0, acorn@npm:^8.4.1, acorn@npm:^8.6.0, acorn@npm:^8.8.2, acorn@npm:^8.9.0":
|
||||||
version: 8.10.0
|
version: 8.10.0
|
||||||
resolution: "acorn@npm:8.10.0"
|
resolution: "acorn@npm:8.10.0"
|
||||||
bin:
|
bin:
|
||||||
|
@ -9610,7 +9620,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"chalk@npm:5.3.0, chalk@npm:^5.3.0":
|
"chalk@npm:5.3.0":
|
||||||
version: 5.3.0
|
version: 5.3.0
|
||||||
resolution: "chalk@npm:5.3.0"
|
resolution: "chalk@npm:5.3.0"
|
||||||
checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80
|
checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80
|
||||||
|
@ -10178,7 +10188,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"commander@npm:^2.12.1, commander@npm:^2.19.0":
|
"commander@npm:^2.12.1, commander@npm:^2.19.0, commander@npm:^2.20.0":
|
||||||
version: 2.20.3
|
version: 2.20.3
|
||||||
resolution: "commander@npm:2.20.3"
|
resolution: "commander@npm:2.20.3"
|
||||||
checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e
|
checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e
|
||||||
|
@ -10755,14 +10765,16 @@ __metadata:
|
||||||
"@types/prompts": ^2.4.4
|
"@types/prompts": ^2.4.4
|
||||||
"@types/validate-npm-package-name": ^4.0.0
|
"@types/validate-npm-package-name": ^4.0.0
|
||||||
"@vitest/coverage-v8": ^0.34.2
|
"@vitest/coverage-v8": ^0.34.2
|
||||||
chalk: ^5.3.0
|
|
||||||
commander: ^11.0.0
|
commander: ^11.0.0
|
||||||
cross-env: ^7.0.3
|
cross-env: ^7.0.3
|
||||||
eslint: ^8.47.0
|
eslint: ^8.47.0
|
||||||
eslint-config-neon: ^0.1.47
|
eslint-config-neon: ^0.1.47
|
||||||
eslint-formatter-pretty: ^5.0.0
|
eslint-formatter-pretty: ^5.0.0
|
||||||
|
fast-glob: ^3.3.1
|
||||||
|
picocolors: ^1.0.0
|
||||||
prettier: ^2.8.8
|
prettier: ^2.8.8
|
||||||
prompts: ^2.4.2
|
prompts: ^2.4.2
|
||||||
|
terser: ^5.19.2
|
||||||
tsup: ^7.2.0
|
tsup: ^7.2.0
|
||||||
typescript: ^5.1.6
|
typescript: ^5.1.6
|
||||||
validate-npm-package-name: ^5.0.0
|
validate-npm-package-name: ^5.0.0
|
||||||
|
@ -22217,7 +22229,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"source-map-support@npm:^0.5.16, source-map-support@npm:^0.5.21":
|
"source-map-support@npm:^0.5.16, source-map-support@npm:^0.5.21, source-map-support@npm:~0.5.20":
|
||||||
version: 0.5.21
|
version: 0.5.21
|
||||||
resolution: "source-map-support@npm:0.5.21"
|
resolution: "source-map-support@npm:0.5.21"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -23074,6 +23086,20 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"terser@npm:^5.19.2":
|
||||||
|
version: 5.19.2
|
||||||
|
resolution: "terser@npm:5.19.2"
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/source-map": ^0.3.3
|
||||||
|
acorn: ^8.8.2
|
||||||
|
commander: ^2.20.0
|
||||||
|
source-map-support: ~0.5.20
|
||||||
|
bin:
|
||||||
|
terser: bin/terser
|
||||||
|
checksum: e059177775b4d4f4cff219ad89293175aefbd1b081252270444dc83e42a2c5f07824eb2a85eae6e22ef6eb7ef04b21af36dd7d1dd7cfb93912310e57d416a205
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"test-exclude@npm:^6.0.0":
|
"test-exclude@npm:^6.0.0":
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
resolution: "test-exclude@npm:6.0.0"
|
resolution: "test-exclude@npm:6.0.0"
|
||||||
|
|
Loading…
Reference in a new issue