diff --git a/package.json b/package.json index 3fe700133..ad2108f56 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "dependencies": { "@sapphire/discord-utilities": "^2.1.5", "@sapphire/discord.js-utilities": "next", - "@sapphire/pieces": "^2.2.0", + "@sapphire/pieces": "^3.0.0", "@sapphire/ratelimits": "^2.0.1", "@sapphire/utilities": "^2.0.1", "lexure": "^0.17.0", diff --git a/src/errorListeners/CoreCommandError.ts b/src/errorListeners/CoreCommandError.ts index cdbb932e7..8fade9f8f 100644 --- a/src/errorListeners/CoreCommandError.ts +++ b/src/errorListeners/CoreCommandError.ts @@ -8,7 +8,7 @@ export class CoreEvent extends Listener { } public run(error: Error, context: CommandErrorPayload) { - const { name, path } = context.piece; - this.container.logger.error(`Encountered error on command "${name}" at path "${path}"`, error); + const { name, location } = context.piece; + this.container.logger.error(`Encountered error on command "${name}" at path "${location.full}"`, error); } } diff --git a/src/errorListeners/CoreEventError.ts b/src/errorListeners/CoreEventError.ts index 7e31b9f57..530eeff3f 100644 --- a/src/errorListeners/CoreEventError.ts +++ b/src/errorListeners/CoreEventError.ts @@ -8,7 +8,7 @@ export class CoreEvent extends Listener { } public run(error: Error, context: ListenerErrorPayload) { - const { name, event, path } = context.piece; - this.container.logger.error(`Encountered error on event listener "${name}" for event "${event}" at path "${path}"`, error); + const { name, event, location } = context.piece; + this.container.logger.error(`Encountered error on event listener "${name}" for event "${event}" at path "${location.full}"`, error); } } diff --git a/src/lib/structures/Command.ts b/src/lib/structures/Command.ts index 1e00ee767..7a5a5ea1e 100644 --- a/src/lib/structures/Command.ts +++ b/src/lib/structures/Command.ts @@ -1,8 +1,7 @@ -import { AliasPiece, AliasPieceOptions, PieceContext } from '@sapphire/pieces'; +import { AliasPiece, AliasPieceJSON, AliasPieceOptions, PieceContext } from '@sapphire/pieces'; import { Awaited, isNullish } from '@sapphire/utilities'; import { Message, PermissionResolvable, Permissions, Snowflake } from 'discord.js'; import * as Lexure from 'lexure'; -import { sep } from 'path'; import { Args } from '../parsers/Args'; import { BucketScope } from '../types/Enums'; import { PreconditionContainerArray, PreconditionEntryResolvable } from '../utils/preconditions/PreconditionContainerArray'; @@ -35,7 +34,7 @@ export abstract class Command extends AliasPiece { * extending this class and overwriting the assignment in the constructor. * @since 2.0.0 */ - public readonly fullCategory: readonly string[] | null = null; + public readonly fullCategory: readonly string[]; /** * The strategy to use for the lexer. @@ -60,6 +59,7 @@ export abstract class Command extends AliasPiece { this.description = options.description ?? ''; this.detailedDescription = options.detailedDescription ?? ''; this.strategy = new FlagUnorderedStrategy(options); + this.fullCategory = options.fullCategory ?? this.location.directories; this.lexer.setQuotes( options.quotes ?? [ @@ -69,19 +69,6 @@ export abstract class Command extends AliasPiece { ] ); - if (options.fullCategory) { - this.fullCategory = options.fullCategory; - } else { - const commandsFolders = [...this.container.stores.get('commands').paths.values()].map((p) => p.split(sep).pop() ?? ''); - const commandPath = context.path.split(sep); - for (const commandFolder of commandsFolders) { - if (commandPath.includes(commandFolder)) { - this.fullCategory = commandPath.slice(commandPath.indexOf(commandFolder) + 1, -1); - break; - } - } - } - if (options.generateDashLessAliases) { const dashLessAliases = []; if (this.name.includes('-')) dashLessAliases.push(this.name.replace(/-/g, '')); @@ -106,44 +93,40 @@ export abstract class Command extends AliasPiece { return new Args(message, this as any, args, context) as any; } - /** - * Get all the main categories of commands. - */ - public get categories(): (string | null)[] { - return Array.from(new Set([...this.container.stores.get('commands').values()].map(({ category }) => category))); - } - /** * The main category for the command, if any. - * This is resolved from {@link Command.fullCategory}, which is automatically - * resolved in the constructor. If you need different logic for category - * then please first look into overwriting {@link Command.fullCategory} before - * looking to overwrite this getter. + * + * This getter retrieves the first value of {@link Command.fullCategory}, if it has at least one item, otherwise it + * returns `null`. + * + * @note You can set {@link CommandOptions.fullCategory} to override the built-in category resolution. */ public get category(): string | null { - return (this.fullCategory?.length ?? 0) > 0 ? this.fullCategory?.[0] ?? null : null; + return this.fullCategory.length > 0 ? this.fullCategory[0] : null; } /** - * The sub category for the command - * This is resolved from {@link Command.fullCategory}, which is automatically - * resolved in the constructor. If you need different logic for category - * then please first look into overwriting {@link Command.fullCategory} before - * looking to overwrite this getter. + * The sub-category for the command, if any. + * + * This getter retrieves the second value of {@link Command.fullCategory}, if it has at least two items, otherwise + * it returns `null`. + * + * @note You can set {@link CommandOptions.fullCategory} to override the built-in category resolution. */ public get subCategory(): string | null { - return (this.fullCategory?.length ?? 0) > 1 ? this.fullCategory?.[1] ?? null : null; + return this.fullCategory.length > 1 ? this.fullCategory[1] : null; } /** - * The parent category for the command - * This is resolved from {@link Command.fullCategory}, which is automatically - * resolved in the constructor. If you need different logic for category - * then please first look into overwriting {@link Command.fullCategory} before - * looking to overwrite this getter. + * The parent category for the command. + * + * This getter retrieves the last value of {@link Command.fullCategory}, if it has at least one item, otherwise it + * returns `null`. + * + * @note You can set {@link CommandOptions.fullCategory} to override the built-in category resolution. */ public get parentCategory(): string | null { - return (this.fullCategory?.length ?? 0) > 0 ? this.fullCategory?.[(this.fullCategory?.length ?? 1) - 1] ?? null : null; + return this.fullCategory.length > 1 ? this.fullCategory[this.fullCategory.length - 1] : null; } /** @@ -156,13 +139,12 @@ export abstract class Command extends AliasPiece { /** * Defines the JSON.stringify behavior of the command. */ - public toJSON(): Record { + public toJSON(): CommandJSON { return { ...super.toJSON(), description: this.description, detailedDescription: this.detailedDescription, - category: this.category, - strategy: this.strategy + category: this.category }; } @@ -503,3 +485,9 @@ export interface CommandContext extends Record { */ commandPrefix: string; } + +export interface CommandJSON extends AliasPieceJSON { + description: string; + detailedDescription: string; + category: string | null; +} diff --git a/src/lib/structures/CommandStore.ts b/src/lib/structures/CommandStore.ts index ff6d80cc7..f2af5f6f4 100644 --- a/src/lib/structures/CommandStore.ts +++ b/src/lib/structures/CommandStore.ts @@ -9,4 +9,13 @@ export class CommandStore extends AliasStore { public constructor() { super(Command as any, { name: 'commands' }); } + + /** + * Get all the command categories. + */ + public get categories(): string[] { + const categories = new Set(this.map((command) => command.category)); + categories.delete(null); + return [...categories] as string[]; + } } diff --git a/src/lib/structures/Listener.ts b/src/lib/structures/Listener.ts index 5aa8e55ba..ae54e2b9d 100644 --- a/src/lib/structures/Listener.ts +++ b/src/lib/structures/Listener.ts @@ -1,4 +1,4 @@ -import { Piece, PieceContext, PieceOptions } from '@sapphire/pieces'; +import { Piece, PieceContext, PieceJSON, PieceOptions } from '@sapphire/pieces'; import type { Client, ClientEvents } from 'discord.js'; import type { EventEmitter } from 'events'; import { Events } from '../types/Events'; @@ -109,7 +109,7 @@ export abstract class Listener exten return super.onUnload(); } - public toJSON(): Record { + public toJSON(): ListenerJSON { return { ...super.toJSON(), once: this.once, @@ -136,3 +136,8 @@ export interface ListenerOptions extends PieceOptions { readonly event?: string; readonly once?: boolean; } + +export interface ListenerJSON extends PieceJSON { + event: string; + once: boolean; +} diff --git a/yarn.lock b/yarn.lock index b8fe0f2f2..13cfe8b7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -805,10 +805,10 @@ dependencies: cross-fetch "^3.1.4" -"@sapphire/pieces@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@sapphire/pieces/-/pieces-2.2.0.tgz#9e45fe1462996d35d513dbafeab713aa5eb4adae" - integrity sha512-VhUMUshM7IvKQ/JXJBUUZYO28fHQQ4+2mxHf2q+Rrm6ZOJAOXrYuplc/JBJ7sHb+Icr8WFhUazlarjKbEjErZw== +"@sapphire/pieces@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sapphire/pieces/-/pieces-3.0.0.tgz#968878dee0c680d00dc29202a4fc15f1daea2b5d" + integrity sha512-Up/ajwm88jLHmG/AKmiEAyBcXlBEV2p96KXYIjoJVX2/bCr+zK+lDWDPE2cg5dJPfU6CABEdMqV5h/wdQxgf9A== dependencies: "@discordjs/collection" "^0.2.1" "@sapphire/utilities" "^2.0.1"