From 4d856c13321b50bc66ea5da1cebb758155ea2170 Mon Sep 17 00:00:00 2001 From: Pkmmte Xeleon Date: Mon, 1 May 2023 00:01:48 -0700 Subject: [PATCH] feat: introducing create-robo New CLI for easily creating new Robo projects just how you want them! Just like the Robo.js framework itself, this utility is also considered pre-release. It may not work perfectly and is likely to undergo changes! --- .changeset/large-walls-carry.md | 5 + packages/create-robo/package.json | 49 ++++ packages/create-robo/src/base-logger.ts | 101 ++++++++ packages/create-robo/src/index.ts | 44 ++++ packages/create-robo/src/logger.ts | 90 +++++++ packages/create-robo/src/robo.ts | 193 +++++++++++++++ packages/create-robo/src/utils.ts | 69 ++++++ .../create-robo/templates/js/.config/robo.mjs | 14 ++ packages/create-robo/templates/js/.gitignore | 32 +++ packages/create-robo/templates/js/README.md | 57 +++++ .../templates/js/src/commands/ping.js | 7 + .../templates/js/src/events/messageCreate.js | 4 + .../create-robo/templates/ts/.config/robo.mjs | 14 ++ packages/create-robo/templates/ts/.gitignore | 32 +++ packages/create-robo/templates/ts/README.md | 57 +++++ .../templates/ts/src/commands/ping.ts | 9 + .../templates/ts/src/events/messageCreate.ts | 4 + packages/create-robo/tsconfig.json | 28 +++ packages/create-robo/tsup.config.ts | 13 + pnpm-lock.yaml | 229 ++++++++++++++++-- 20 files changed, 1030 insertions(+), 21 deletions(-) create mode 100644 .changeset/large-walls-carry.md create mode 100644 packages/create-robo/package.json create mode 100644 packages/create-robo/src/base-logger.ts create mode 100644 packages/create-robo/src/index.ts create mode 100644 packages/create-robo/src/logger.ts create mode 100644 packages/create-robo/src/robo.ts create mode 100644 packages/create-robo/src/utils.ts create mode 100644 packages/create-robo/templates/js/.config/robo.mjs create mode 100644 packages/create-robo/templates/js/.gitignore create mode 100644 packages/create-robo/templates/js/README.md create mode 100644 packages/create-robo/templates/js/src/commands/ping.js create mode 100644 packages/create-robo/templates/js/src/events/messageCreate.js create mode 100644 packages/create-robo/templates/ts/.config/robo.mjs create mode 100644 packages/create-robo/templates/ts/.gitignore create mode 100644 packages/create-robo/templates/ts/README.md create mode 100644 packages/create-robo/templates/ts/src/commands/ping.ts create mode 100644 packages/create-robo/templates/ts/src/events/messageCreate.ts create mode 100644 packages/create-robo/tsconfig.json create mode 100644 packages/create-robo/tsup.config.ts diff --git a/.changeset/large-walls-carry.md b/.changeset/large-walls-carry.md new file mode 100644 index 00000000..935c5cc9 --- /dev/null +++ b/.changeset/large-walls-carry.md @@ -0,0 +1,5 @@ +--- +'create-robo': minor +--- + +feat: introducing create-robo diff --git a/packages/create-robo/package.json b/packages/create-robo/package.json new file mode 100644 index 00000000..68849e07 --- /dev/null +++ b/packages/create-robo/package.json @@ -0,0 +1,49 @@ +{ + "name": "create-robo", + "version": "0.0.0", + "private": false, + "description": "Create Robo.js Discord bots with one command", + "engines": { + "node": ">=18.0.0" + }, + "scripts": { + "build": "tsup" + }, + "bin": { + "create-robo": "dist/index.js" + }, + "keywords": [], + "license": "MIT", + "author": "WavePlay (waveplay.com)", + "contributors": [ + "Pkmmte Xeleon " + ], + "type": "module", + "files": [ + "dist/", + "src/", + "templates/", + "LICENSE", + "README.md" + ], + "repository": { + "type": "git", + "url": "https://github.com/Wave-Play/robo.git", + "directory": "packages/create-robo" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "dependencies": { + "chalk": "5.2.0", + "commander": "10.0.0", + "inquirer": "^9.2.0" + }, + "devDependencies": { + "@types/inquirer": "^9.0.3", + "@types/node": "^18.16.3", + "tsup": "6.7.0", + "typescript": "5.0.2" + } +} diff --git a/packages/create-robo/src/base-logger.ts b/packages/create-robo/src/base-logger.ts new file mode 100644 index 00000000..c4dd82a3 --- /dev/null +++ b/packages/create-robo/src/base-logger.ts @@ -0,0 +1,101 @@ +import chalk from 'chalk' + +export type LogLevel = 'trace' | 'debug' | 'info' | 'wait' | 'other' | 'event' | 'ready' | 'warn' | 'error' + +export interface BaseLoggerOptions { + enabled?: boolean + level?: LogLevel +} + +abstract class BaseLogger { + protected _enabled: boolean + protected _level: LogLevel + + constructor(options?: BaseLoggerOptions) { + const { enabled = true, level } = options ?? {} + + this._enabled = enabled + this._level = level ?? 'info' + } + + public trace(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.trace) { + this._log('trace', ...data) + } + } + + public debug(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.debug) { + this._log('debug', ...data) + } + } + + public info(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.info) { + this._log('info', ...data) + } + } + + public wait(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.wait) { + this._log('wait', ...data) + } + } + + public log(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.other) { + this._log('other', ...data) + } + } + + public event(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.event) { + this._log('event', ...data) + } + } + + public ready(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.ready) { + this._log('ready', ...data) + } + } + + public warn(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.warn) { + this._log('warn', ...data) + } + } + + public error(...data: unknown[]) { + if (this._enabled && LogLevelValues[this._level] <= LogLevelValues.error) { + this._log('error', ...data) + } + } + + protected abstract _log(level: LogLevel, ...data: unknown[]): void +} + +const LogLevelValues: Record = { + trace: 0, + debug: 1, + info: 2, + wait: 2, + other: 2, + event: 3, + ready: 5, + warn: 4, + error: 5 +} + +const colorizedLogLevels = { + trace: chalk.gray('trace'.padEnd(5)), + debug: chalk.cyan('debug'.padEnd(5)), + info: chalk.blue('info'.padEnd(5)), + wait: chalk.cyan('wait'.padEnd(5)), + event: chalk.magenta('event'.padEnd(5)), + ready: chalk.green('ready'.padEnd(5)), + warn: chalk.yellow('warn'.padEnd(5)), + error: chalk.red('error'.padEnd(5)) +} + +export { BaseLogger, colorizedLogLevels } diff --git a/packages/create-robo/src/index.ts b/packages/create-robo/src/index.ts new file mode 100644 index 00000000..89f1b204 --- /dev/null +++ b/packages/create-robo/src/index.ts @@ -0,0 +1,44 @@ +#!/usr/bin/env node +import { Command } from 'commander' +import { createRequire } from 'node:module' +import Robo from './robo.js' +import { logger } from './logger.js' + +// Read the version from the package.json file +const require = createRequire(import.meta.url) +const packageJson = require('../package.json') + +interface CommandOptions { + verbose?: boolean +} + +new Command('create-robo ') + .description('Create a new Robo project') + .version(packageJson.version) + .option('-v --verbose', 'print more information for debugging') + .action(async (options: CommandOptions, { args }) => { + logger({ + level: options.verbose ? 'debug' : 'info' + }).info(`Creating new Robo.js project...\n`) + const projectName = args[0] + const robo = new Robo(projectName) + + // Get user input to determine which features to include or use the recommended defaults + const selectedFeaturesOrDefaults = await robo.getUserInput() + if (selectedFeaturesOrDefaults === 'defaults') { + await robo.createPackage(['TypeScript', 'ESLint', 'Prettier']) + } else { + await robo.createPackage(selectedFeaturesOrDefaults) + } + + // Determine if TypeScript is selected and copy the corresponding template files + const useTypeScript = + selectedFeaturesOrDefaults === 'defaults' || (selectedFeaturesOrDefaults as string[]).includes('TypeScript') + await robo.copyTemplateFiles('', useTypeScript) + + // Ask the user for their Discord credentials (token and client ID) and store them for later use + await robo.askForDiscordCredentials() + logger.log('') + logger.ready(`Successfully created ${projectName}. Happy coding!`) + }) + .parse(process.argv) diff --git a/packages/create-robo/src/logger.ts b/packages/create-robo/src/logger.ts new file mode 100644 index 00000000..1b36026a --- /dev/null +++ b/packages/create-robo/src/logger.ts @@ -0,0 +1,90 @@ +import { BaseLogger, LogLevel, colorizedLogLevels } from './base-logger.js' +import type { BaseLoggerOptions } from './base-logger.js' + +class Logger extends BaseLogger { + constructor(options?: BaseLoggerOptions) { + super(options) + } + + protected _log(level: LogLevel, ...data: unknown[]): void { + // Format the message all pretty and stuff + if (level !== 'other') { + const colorizedLevel = colorizedLogLevels[level] + data.unshift((colorizedLevel ?? level.padEnd(5)) + ' -') + } + + switch (level) { + case 'trace': + case 'debug': + console.debug(...data) + break + case 'info': + console.info(...data) + break + case 'wait': + console.info(...data) + break + case 'event': + console.log(...data) + break + case 'warn': + console.warn(...data) + break + case 'error': + console.error(...data) + break + default: + console.log(...data) + } + } +} + +export { Logger } + +let _logger: Logger | null = null + +export function logger(options?: BaseLoggerOptions): Logger { + if (options) { + _logger = new Logger(options) + } else if (!_logger) { + _logger = new Logger() + } + + return _logger +} + +logger.trace = function (...data: unknown[]): void { + return logger().trace(...data) +} + +logger.debug = function (...data: unknown[]): void { + return logger().debug(...data) +} + +logger.info = function (...data: unknown[]): void { + return logger().info(...data) +} + +logger.wait = function (...data: unknown[]): void { + return logger().wait(...data) +} + +logger.log = function (...data: unknown[]): void { + return logger().log(...data) +} + +logger.event = function (...data: unknown[]): void { + return logger().event(...data) +} + +logger.ready = function (...data: unknown[]): void { + return logger().ready(...data) +} + +logger.warn = function (...data: unknown[]): void { + return logger().warn(...data) +} + +logger.error = function (...data: unknown[]): void { + return logger().error(...data) +} diff --git a/packages/create-robo/src/robo.ts b/packages/create-robo/src/robo.ts new file mode 100644 index 00000000..5e902cf4 --- /dev/null +++ b/packages/create-robo/src/robo.ts @@ -0,0 +1,193 @@ +import fs from 'fs/promises' +import path from 'path' +import inquirer from 'inquirer' +import chalk from 'chalk' +import { fileURLToPath } from 'node:url' +import { exec, getPackageManager, hasProperties } from './utils.js' +import { logger } from './logger.js' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +interface PackageJson { + name: string + version: string + description: string + engines: { + node: string + } + scripts: Record + dependencies: { + 'discord.js': string + '@roboplay/robo.js': string + [key: string]: string + } + devDependencies: { + [key: string]: string + } +} + +export default class Robo { + private _name: string + private _workingDir: string + + constructor(name: string) { + this._name = name + this._workingDir = path.join(process.cwd(), name) + } + + async getUserInput(): Promise<'defaults' | string[]> { + const { useDefaults } = await inquirer.prompt([ + { + type: 'list', + name: 'useDefaults', + message: 'Choose an option:', + choices: [ + { + name: 'Use recommended defaults', + value: 'defaults' + }, + { + name: 'Customize features', + value: 'custom' + } + ] + } + ]) + + if (useDefaults === 'defaults') { + return 'defaults' + } + + const { selectedFeatures } = await inquirer.prompt([ + { + type: 'checkbox', + name: 'selectedFeatures', + message: 'Select features:', + choices: [{ name: 'TypeScript' }, { name: 'ESLint' }, { name: 'Prettier' }] + } + ]) + + return selectedFeatures + } + + async createPackage(features: string[]): Promise { + // Find the package manager that triggered this command + const packageManager = getPackageManager() + logger.debug(`Using ${chalk.bold(packageManager)} in ${this._workingDir}...`) + + // Create a package.json file based on the selected features + const packageJson: PackageJson = { + name: this._name, + version: '0.1.0', + description: '', + engines: { + node: '>=18.0.0' + }, + scripts: { + build: 'robo build', + deploy: 'robo deploy', + dev: 'robo dev', + doctor: 'robo doctor', + start: 'robo start' + }, + dependencies: { + 'discord.js': '^14.7.1', + '@roboplay/robo.js': 'latest' + }, + devDependencies: {} + } + + const runPrefix = packageManager + packageManager === 'npm' ? 'npm run ' : packageManager + ' ' + if (features.includes('TypeScript')) { + packageJson.devDependencies['typescript'] = '^5.0.0' + } + if (features.includes('ESLint')) { + packageJson.devDependencies['eslint'] = '^8.36.0' + packageJson.scripts['lint'] = runPrefix + 'lint:eslint' + packageJson.scripts['lint:eslint'] = 'eslint . --ext js,jsx,ts,tsx' + } + if (features.includes('Prettier')) { + packageJson.devDependencies['prettier'] = '^2.8.5' + packageJson.scripts['lint:style'] = 'prettier --write .' + + const hasLintScript = packageJson.scripts['lint'] + if (hasLintScript) { + packageJson.scripts['lint'] += ' && ' + runPrefix + 'lint:style' + } + } + await fs.mkdir(this._workingDir, { recursive: true }) + await fs.writeFile(path.join(this._workingDir, 'package.json'), JSON.stringify(packageJson, null, 2)) + + // Install dependencies using the package manager that triggered the command + await exec(`${packageManager} install`, { cwd: this._workingDir }) + } + + async copyTemplateFiles(sourceDir: string, useTypeScript: boolean): Promise { + const templateDir = useTypeScript ? '../templates/ts' : '../templates/js' + const sourcePath = path.join(__dirname, templateDir, sourceDir) + const targetPath = path.join(this._workingDir, sourceDir) + + const items = await fs.readdir(sourcePath) + + for (const item of items) { + const itemSourcePath = path.join(sourcePath, item) + const itemTargetPath = path.join(targetPath, item) + const stat = await fs.stat(itemSourcePath) + + if (stat.isDirectory()) { + await fs.mkdir(itemTargetPath, { recursive: true }) + await this.copyTemplateFiles(path.join(sourceDir, item), useTypeScript) + } else { + await fs.copyFile(itemSourcePath, itemTargetPath) + } + } + } + + async askForDiscordCredentials(): Promise { + logger.log('\n') + logger.info( + chalk.blue('To get your Discord Token and Client ID, register your bot at the Discord Developer portal.') + ) + logger.info(`Discord Developer Portal: ${chalk.bold.underline('https://discord.com/developers/applications')}\n`) + + const { discordToken, discordClientId } = await inquirer.prompt([ + { + type: 'input', + name: 'discordToken', + message: 'Enter your Discord Token (press Enter to skip):' + }, + { + type: 'input', + name: 'discordClientId', + message: 'Enter your Discord Client ID (press Enter to skip):' + } + ]) + + const envFilePath = path.join(this._workingDir, '.env') + let envContent = '' + + try { + envContent = await fs.readFile(envFilePath, 'utf8') + } catch (error) { + if (hasProperties(error, ['code']) && error.code !== 'ENOENT') { + throw error + } + } + + // Helper function to update or add a variable + const updateOrAddVariable = (content: string, variable: string, value: string): string => { + const regex = new RegExp(`(${variable}\\s*=)(.*)`, 'i') + if (regex.test(content)) { + return content.replace(regex, `$1${value}`) + } else { + return `${content}${variable}="${value}"\n` + } + } + + // Update DISCORD_TOKEN and DISCORD_CLIENT_ID variables + envContent = updateOrAddVariable(envContent, 'DISCORD_TOKEN', discordToken ?? '') + envContent = updateOrAddVariable(envContent, 'DISCORD_CLIENT_ID', discordClientId ?? '') + + await fs.writeFile(envFilePath, envContent) + } +} diff --git a/packages/create-robo/src/utils.ts b/packages/create-robo/src/utils.ts new file mode 100644 index 00000000..555e36e9 --- /dev/null +++ b/packages/create-robo/src/utils.ts @@ -0,0 +1,69 @@ +import { spawn } from 'child_process' +import { logger } from './logger.js' +import type { SpawnOptions } from 'child_process' +import chalk from 'chalk' + +type PackageManager = 'npm' | 'pnpm' | 'yarn' + +export const IS_WINDOWS = /^win/.test(process.platform) + +/** + * Eh, just Windows things + */ +export function cmd(packageManager: PackageManager): string { + return IS_WINDOWS ? `${packageManager}.cmd` : packageManager +} + +/** + * Run a command as a child process + */ +export function exec(command: string, options?: SpawnOptions) { + return new Promise((resolve, reject) => { + logger.debug(`> ${chalk.bold(command)}`) + + // Run command as child process + const args = command.split(' ') + const childProcess = spawn(args.shift(), args, { + ...options ?? {}, + env: { ...process.env, FORCE_COLOR: '1' }, + stdio: 'inherit' + }) + + // Resolve promise when child process exits + childProcess.on('close', (code) => { + if (code === 0) { + resolve() + } else { + reject(new Error(`Child process exited with code ${code}`)) + } + }) + + // Or reject when it errors + childProcess.on('error', (error) => { + reject(error) + }) + }) +} + +/** + * Get the package manager used to run this CLI + * This allows developers to use their preferred package manager seamlessly + */ +export function getPackageManager(): PackageManager { + const userAgent = process.env.npm_config_user_agent + + if (userAgent?.startsWith('yarn')) { + return 'yarn' + } else if (userAgent?.startsWith('pnpm')) { + return 'pnpm' + } else { + return 'npm' + } +} + +export function hasProperties>( + obj: unknown, + props: (keyof T)[] +): obj is T & Record { + return typeof obj === 'object' && obj !== null && props.every((prop) => prop in obj) +} diff --git a/packages/create-robo/templates/js/.config/robo.mjs b/packages/create-robo/templates/js/.config/robo.mjs new file mode 100644 index 00000000..e69994c1 --- /dev/null +++ b/packages/create-robo/templates/js/.config/robo.mjs @@ -0,0 +1,14 @@ +// @ts-check + +/** + * @type {import('@roboplay/robo.js').Config} + **/ +export default { + clientOptions: { + intents: [ + 'Guilds', + 'GuildMessages' + ] + }, + plugins: [] +} diff --git a/packages/create-robo/templates/js/.gitignore b/packages/create-robo/templates/js/.gitignore new file mode 100644 index 00000000..1405b072 --- /dev/null +++ b/packages/create-robo/templates/js/.gitignore @@ -0,0 +1,32 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# robo +.robo/* +!.robo/manifest.json + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env +.env*.local + +# typescript +*.tsbuildinfo +*.json +.gitattributes diff --git a/packages/create-robo/templates/js/README.md b/packages/create-robo/templates/js/README.md new file mode 100644 index 00000000..57b516ee --- /dev/null +++ b/packages/create-robo/templates/js/README.md @@ -0,0 +1,57 @@ +# Hello Robo + +Welcome to your fresh Robo.js project! A Node framework for Discord.js bots, Robo.js handles boilerplate, automates command registration, simplifies Typescript support, and boasts "Sage" for easy interactions. Empowered by a dynamic plugin system, your robo thrives on RoboPlay or any Node-supporting host. + +Let's get started on your journey to create the perfect Discord bot! + +## Running 🏃‍♂️ + +To run your robo, simply use the `robo dev` command like so: + +```bash +npm run dev +``` + +No need to re-run when you make changes. Your robo will automatically restart! 🔄 + +Ready to deploy and keep your robo online at all times? Check out the [Deployment](#deployment) section. + +## Building 🏗️ + +Create new slash commands by making a new file under the `/src/commands` directory with an exported default function. The file's name becomes the command's name. You can either use the `interaction` parameter or return the result to let Sage handle it for you. For more info on commands, see the [Discord.js documentation](https://discord.js.org/#/docs/main/stable/general/welcome). + +Commands will be automatically registered with Discord when needed, but you can force it by running `robo build -f`. + +To listen to new events, create a file named after the event in `/src/events`. For example, `typingStart.js` will notify you when someone starts typing. You can stack multiple files for the same event by making a directory named after the event. Files inside it can be named whatever you want. For example: + +``` +- src + - events + - typingStart + - your-file.js + - another.js +``` + +## Configuration ⚙️ + +Robo.js automatically handles creating your Discord.js `Client` instance, but you can still configure what gets passed to it using the `.config/robo.mjs` file. Use it to add more intents or change the behavior of other Robo.js features such as Sage, default commands, timeouts, and more. + +The `.env` file contains your `DISCORD_TOKEN` and `DISCORD_CLIENT_ID`. Keep these secret. You can get these values from the [Discord Developer Portal](https://discord.com/developers/applications). + +## Plugins 🔌 + +Robo.js has a powerful plugin system. Install plugins as NPM packages like this: + +```bash +npm install @roboplay/plugin-gpt +``` + +Replace `@roboplay/plugin-gpt` with the plugin's package name. You can also turn your existing robo into a plugin using `robo build plugin` and uploading it to NPM via `npm publish`. Just be careful and make sure you're not including sensitive data such as your `.env` file. + +## Deployment 🚀 + +Run the `robo deploy` command to automatically deploy to RoboPlay for free once you're ready to keep your robo online 24/7. You can also self-host your robo anywhere that supports Node. Just make sure to run the `robo build` command followed by `robo start`. + +You can also run `robo invite` (beta) to automatically generate a server invite to test it yourself or show it off! You can also use the [Discord Developer Portal](https://discord.com/developers/applications) to generate an invite. + +Happy coding! 🎉 diff --git a/packages/create-robo/templates/js/src/commands/ping.js b/packages/create-robo/templates/js/src/commands/ping.js new file mode 100644 index 00000000..a722a376 --- /dev/null +++ b/packages/create-robo/templates/js/src/commands/ping.js @@ -0,0 +1,7 @@ +export const config = { + description: 'Replies with Pong!' +} + +export default () => { + return 'Pong!' +} diff --git a/packages/create-robo/templates/js/src/events/messageCreate.js b/packages/create-robo/templates/js/src/events/messageCreate.js new file mode 100644 index 00000000..d3199745 --- /dev/null +++ b/packages/create-robo/templates/js/src/events/messageCreate.js @@ -0,0 +1,4 @@ +export default () => { + // Your code here... + // This gets called when your bot detects a new message +} diff --git a/packages/create-robo/templates/ts/.config/robo.mjs b/packages/create-robo/templates/ts/.config/robo.mjs new file mode 100644 index 00000000..e69994c1 --- /dev/null +++ b/packages/create-robo/templates/ts/.config/robo.mjs @@ -0,0 +1,14 @@ +// @ts-check + +/** + * @type {import('@roboplay/robo.js').Config} + **/ +export default { + clientOptions: { + intents: [ + 'Guilds', + 'GuildMessages' + ] + }, + plugins: [] +} diff --git a/packages/create-robo/templates/ts/.gitignore b/packages/create-robo/templates/ts/.gitignore new file mode 100644 index 00000000..1405b072 --- /dev/null +++ b/packages/create-robo/templates/ts/.gitignore @@ -0,0 +1,32 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# robo +.robo/* +!.robo/manifest.json + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env +.env*.local + +# typescript +*.tsbuildinfo +*.json +.gitattributes diff --git a/packages/create-robo/templates/ts/README.md b/packages/create-robo/templates/ts/README.md new file mode 100644 index 00000000..57b516ee --- /dev/null +++ b/packages/create-robo/templates/ts/README.md @@ -0,0 +1,57 @@ +# Hello Robo + +Welcome to your fresh Robo.js project! A Node framework for Discord.js bots, Robo.js handles boilerplate, automates command registration, simplifies Typescript support, and boasts "Sage" for easy interactions. Empowered by a dynamic plugin system, your robo thrives on RoboPlay or any Node-supporting host. + +Let's get started on your journey to create the perfect Discord bot! + +## Running 🏃‍♂️ + +To run your robo, simply use the `robo dev` command like so: + +```bash +npm run dev +``` + +No need to re-run when you make changes. Your robo will automatically restart! 🔄 + +Ready to deploy and keep your robo online at all times? Check out the [Deployment](#deployment) section. + +## Building 🏗️ + +Create new slash commands by making a new file under the `/src/commands` directory with an exported default function. The file's name becomes the command's name. You can either use the `interaction` parameter or return the result to let Sage handle it for you. For more info on commands, see the [Discord.js documentation](https://discord.js.org/#/docs/main/stable/general/welcome). + +Commands will be automatically registered with Discord when needed, but you can force it by running `robo build -f`. + +To listen to new events, create a file named after the event in `/src/events`. For example, `typingStart.js` will notify you when someone starts typing. You can stack multiple files for the same event by making a directory named after the event. Files inside it can be named whatever you want. For example: + +``` +- src + - events + - typingStart + - your-file.js + - another.js +``` + +## Configuration ⚙️ + +Robo.js automatically handles creating your Discord.js `Client` instance, but you can still configure what gets passed to it using the `.config/robo.mjs` file. Use it to add more intents or change the behavior of other Robo.js features such as Sage, default commands, timeouts, and more. + +The `.env` file contains your `DISCORD_TOKEN` and `DISCORD_CLIENT_ID`. Keep these secret. You can get these values from the [Discord Developer Portal](https://discord.com/developers/applications). + +## Plugins 🔌 + +Robo.js has a powerful plugin system. Install plugins as NPM packages like this: + +```bash +npm install @roboplay/plugin-gpt +``` + +Replace `@roboplay/plugin-gpt` with the plugin's package name. You can also turn your existing robo into a plugin using `robo build plugin` and uploading it to NPM via `npm publish`. Just be careful and make sure you're not including sensitive data such as your `.env` file. + +## Deployment 🚀 + +Run the `robo deploy` command to automatically deploy to RoboPlay for free once you're ready to keep your robo online 24/7. You can also self-host your robo anywhere that supports Node. Just make sure to run the `robo build` command followed by `robo start`. + +You can also run `robo invite` (beta) to automatically generate a server invite to test it yourself or show it off! You can also use the [Discord Developer Portal](https://discord.com/developers/applications) to generate an invite. + +Happy coding! 🎉 diff --git a/packages/create-robo/templates/ts/src/commands/ping.ts b/packages/create-robo/templates/ts/src/commands/ping.ts new file mode 100644 index 00000000..df149161 --- /dev/null +++ b/packages/create-robo/templates/ts/src/commands/ping.ts @@ -0,0 +1,9 @@ +import type { CommandConfig } from '@roboplay/robo.js' + +export const config: CommandConfig = { + description: 'Replies with Pong!' +} + +export default () => { + return 'Pong!' +} diff --git a/packages/create-robo/templates/ts/src/events/messageCreate.ts b/packages/create-robo/templates/ts/src/events/messageCreate.ts new file mode 100644 index 00000000..d3199745 --- /dev/null +++ b/packages/create-robo/templates/ts/src/events/messageCreate.ts @@ -0,0 +1,4 @@ +export default () => { + // Your code here... + // This gets called when your bot detects a new message +} diff --git a/packages/create-robo/tsconfig.json b/packages/create-robo/tsconfig.json new file mode 100644 index 00000000..64533182 --- /dev/null +++ b/packages/create-robo/tsconfig.json @@ -0,0 +1,28 @@ +{ + "include": ["src"], + "compilerOptions": { + "target": "ESNext", + "module": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "types": ["node"], + "baseUrl": ".", + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": false, + "forceConsistentCasingInFileNames": true, + "noEmit": false, + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "jsx": "react-jsx", + "importHelpers": true, + "declaration": true, + "sourceMap": true, + "rootDir": ".", + "noUnusedLocals": true, + "moduleResolution": "node", + "outDir": "dist" + } +} diff --git a/packages/create-robo/tsup.config.ts b/packages/create-robo/tsup.config.ts new file mode 100644 index 00000000..d18d7347 --- /dev/null +++ b/packages/create-robo/tsup.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src'], + outDir: 'dist', + format: ['esm'], + bundle: false, + clean: false, + dts: false, + minify: false, + sourcemap: true, + treeshake: true +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9cabe062..60538d1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,25 @@ importers: turbo: 1.8.4 typescript: 5.0.2 + packages/create-robo: + specifiers: + '@types/inquirer': ^9.0.3 + '@types/node': ^18.16.3 + chalk: 5.2.0 + commander: 10.0.0 + inquirer: ^9.2.0 + tsup: 6.7.0 + typescript: 5.0.2 + dependencies: + chalk: 5.2.0 + commander: 10.0.0 + inquirer: 9.2.0 + devDependencies: + '@types/inquirer': 9.0.3 + '@types/node': 18.16.3 + tsup: 6.7.0_typescript@5.0.2 + typescript: 5.0.2 + packages/discord: specifiers: '@swc/core': 1.3.41 @@ -881,7 +900,7 @@ packages: /@types/concat-stream/2.0.0: resolution: {integrity: sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==} dependencies: - '@types/node': 18.15.5 + '@types/node': 18.16.3 dev: true /@types/debug/4.1.7: @@ -910,6 +929,13 @@ packages: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} dev: true + /@types/inquirer/9.0.3: + resolution: {integrity: sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==} + dependencies: + '@types/through': 0.0.30 + rxjs: 7.8.1 + dev: true + /@types/is-ci/3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: @@ -955,6 +981,10 @@ packages: resolution: {integrity: sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==} dev: true + /@types/node/18.16.3: + resolution: {integrity: sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==} + dev: true + /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -977,6 +1007,12 @@ packages: minipass: 4.2.8 dev: true + /@types/through/0.0.30: + resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} + dependencies: + '@types/node': 18.16.3 + dev: true + /@types/unist/2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true @@ -1185,6 +1221,13 @@ packages: engines: {node: '>=6'} dev: false + /ansi-escapes/6.2.0: + resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==} + engines: {node: '>=14.16'} + dependencies: + type-fest: 3.8.0 + dev: false + /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1192,7 +1235,6 @@ packages: /ansi-regex/6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: true /ansi-styles/3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} @@ -1209,7 +1251,6 @@ packages: /ansi-styles/6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: true /any-promise/1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -1290,6 +1331,10 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /base64-js/1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false + /better-path-resolve/1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -1301,6 +1346,14 @@ packages: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} + /bl/5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.2 + dev: false + /boxen/7.0.2: resolution: {integrity: sha512-1Z4UJabXUP1/R9rLpoU3O2lEMnG3pPLAs/ZD2lF3t2q7qD5lM8rqbtnvtvm4N0wEyNlE+9yZVTVAGmd1V5jabg==} engines: {node: '>=14.16'} @@ -1357,6 +1410,13 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true + /buffer/6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: false + /bundle-require/4.0.1_esbuild@0.17.17: resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1508,6 +1568,23 @@ packages: engines: {node: '>=10'} dev: true + /cli-cursor/4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + restore-cursor: 4.0.0 + dev: false + + /cli-spinners/2.8.0: + resolution: {integrity: sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ==} + engines: {node: '>=6'} + dev: false + + /cli-width/4.0.0: + resolution: {integrity: sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw==} + engines: {node: '>= 12'} + dev: false + /cliui/6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -1820,14 +1897,12 @@ packages: /eastasianwidth/0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} /emoji-regex/9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true /end-of-stream/1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -1963,7 +2038,6 @@ packages: /escape-string-regexp/5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - dev: true /eslint-scope/5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} @@ -2167,6 +2241,14 @@ packages: format: 0.2.2 dev: true + /figures/5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + dev: false + /file-entry-cache/6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -2677,7 +2759,6 @@ packages: /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true /ignore/5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} @@ -2724,7 +2805,6 @@ packages: /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: true /ini/1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} @@ -2740,6 +2820,27 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dev: true + /inquirer/9.2.0: + resolution: {integrity: sha512-WWERbVqjsTXjXub1ZW0ZHDit1dyHqy0T9XIkky9TnmKAPrjU9Jkd59nZPK0dUuM3s73GZAZu2Jo4iFU3XSPVLA==} + engines: {node: '>=14.18.0'} + dependencies: + ansi-escapes: 6.2.0 + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-width: 4.0.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 1.0.0 + ora: 6.3.0 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 5.1.2 + strip-ansi: 7.0.1 + through: 2.3.8 + wrap-ansi: 8.1.0 + dev: false + /internal-slot/1.0.5: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} @@ -2853,6 +2954,11 @@ packages: is-path-inside: 3.0.3 dev: true + /is-interactive/2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + dev: false + /is-negative-zero/2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -2948,6 +3054,11 @@ packages: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true + /is-unicode-supported/1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + dev: false + /is-weakref/1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: @@ -3135,7 +3246,14 @@ packages: /lodash/4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true + + /log-symbols/5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + dependencies: + chalk: 5.2.0 + is-unicode-supported: 1.3.0 + dev: false /longest-streak/3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -3786,7 +3904,6 @@ packages: /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - dev: true /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} @@ -3882,6 +3999,11 @@ packages: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /mute-stream/1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: false + /mz/2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -4030,7 +4152,6 @@ packages: engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 - dev: true /openai/3.2.1: resolution: {integrity: sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==} @@ -4053,6 +4174,21 @@ packages: word-wrap: 1.2.3 dev: true + /ora/6.3.0: + resolution: {integrity: sha512-1/D8uRFY0ay2kgBpmAwmSA404w4OoPVhHMqRqtjvrcK/dnzcEZxMJ+V4DUbyICu8IIVRclHcOf5wlD1tMY4GUQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + chalk: 5.2.0 + cli-cursor: 4.0.0 + cli-spinners: 2.8.0 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + stdin-discarder: 0.1.0 + strip-ansi: 7.0.1 + wcwidth: 1.0.1 + dev: false + /os-tmpdir/1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -4464,7 +4600,6 @@ packages: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - dev: true /readable-web-to-node-stream/3.0.2: resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} @@ -4638,6 +4773,14 @@ packages: lowercase-keys: 3.0.0 dev: true + /restore-cursor/4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: false + /retext-english/4.1.0: resolution: {integrity: sha512-Pky2idjvgkzfodO0GH9X4IU8LX/d4ULTnLf7S1WsBRlSCh/JdTFPafXZstJqZehtQWNHrgoCqVOiGugsNFYvIQ==} dependencies: @@ -4694,11 +4837,21 @@ packages: fsevents: 2.3.2 dev: true + /run-async/2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: false + /run-parallel/1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + /rxjs/7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.5.0 + /sade/1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} @@ -4708,7 +4861,6 @@ packages: /safe-buffer/5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true /safe-regex-test/1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} @@ -4864,6 +5016,13 @@ packages: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: false + /stdin-discarder/0.1.0: + resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + bl: 5.1.0 + dev: false + /stream-combiner/0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} dependencies: @@ -4896,7 +5055,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.0.1 - dev: true /string.prototype.trim/1.2.7: resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} @@ -4931,7 +5089,6 @@ packages: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 - dev: true /stringify-entities/4.0.3: resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==} @@ -4951,7 +5108,6 @@ packages: engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: true /strip-bom/3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -5066,7 +5222,6 @@ packages: /through/2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true /through2/0.4.2: resolution: {integrity: sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==} @@ -5149,6 +5304,41 @@ packages: /tslib/2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + + /tsup/6.7.0_typescript@5.0.2: + resolution: {integrity: sha512-L3o8hGkaHnu5TdJns+mCqFsDBo83bJ44rlK7e6VdanIvpea4ArPcU3swWGsLVbXak1PqQx/V+SSmFPujBK+zEQ==} + engines: {node: '>=14.18'} + hasBin: true + peerDependencies: + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.1.0' + peerDependenciesMeta: + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + dependencies: + bundle-require: 4.0.1_esbuild@0.17.17 + cac: 6.7.14 + chokidar: 3.5.3 + debug: 4.3.4 + esbuild: 0.17.17 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4 + resolve-from: 5.0.0 + rollup: 3.20.3 + source-map: 0.8.0-beta.0 + sucrase: 3.32.0 + tree-kill: 1.2.2 + typescript: 5.0.2 + transitivePeerDependencies: + - supports-color + - ts-node dev: true /tsup/6.7.0_w6avwkdklr2pf4o6fcw5zz7r3e: @@ -5313,7 +5503,6 @@ packages: /type-fest/3.8.0: resolution: {integrity: sha512-FVNSzGQz9Th+/9R6Lvv7WIAkstylfHN2/JYxkyhhmKFYh9At2DST8t6L6Lref9eYO8PXFTfG9Sg1Agg0K3vq3Q==} engines: {node: '>=14.16'} - dev: true /typed-array-length/1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} @@ -5374,7 +5563,7 @@ packages: '@types/concat-stream': 2.0.0 '@types/debug': 4.1.7 '@types/is-empty': 1.2.1 - '@types/node': 18.15.5 + '@types/node': 18.16.3 '@types/unist': 2.0.6 concat-stream: 2.0.0 debug: 4.3.4 @@ -5550,7 +5739,6 @@ packages: /util-deprecate/1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true /uvu/0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} @@ -5738,7 +5926,6 @@ packages: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.0.1 - dev: true /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}