Skip to content

Commit 93c2c29

Browse files
authored
feat: Switch from dynamic import to a more explicit approach (#3)
* feat: Switch from dynamic import to a more explicit approach * fix: Fix deploy.ts to use the exported commands Map * feat: Add typecheck script and remove zod * feat: remove zod * feat: create a file for event types * feat: create event utils * feat: add events barrel export * feat: add commands type * feat: add commands utils * feat: add commands barrel export * refactor: adjust imports paths * refactor: adjust events import paths * refactor: update index.ts to accomodate barrel exports and utils * fix: fix bug in rate limiter * feat: remove loaders files, logic moved to other files
1 parent b54bfee commit 93c2c29

File tree

20 files changed

+138
-338
lines changed

20 files changed

+138
-338
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"format": "biome format --write .",
1717
"check": "biome check .",
1818
"check:fix": "biome check --write .",
19+
"typecheck": "tsc --noEmit",
1920
"test": "node --test",
2021
"prepare": "husky",
2122
"pre-commit": "lint-staged"
@@ -35,8 +36,7 @@
3536
"lint-staged": "^16.2.1",
3637
"tsup": "^8.5.0",
3738
"tsx": "^4.20.6",
38-
"typescript": "^5.9.2",
39-
"zod": "^4.1.11"
39+
"typescript": "^5.9.2"
4040
},
4141
"lint-staged": {
4242
"*.{ts,js}": [

pnpm-lock.yaml

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/docs/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { ApplicationCommandOptionType } from 'discord.js';
2-
import { createCommands } from '../index.js';
2+
import { createCommands } from '../../util/commands.js';
33
import { type DocProvider, docProviders, executeDocCommand } from './providers.js';
44

5-
export default createCommands(
5+
export const docsCommands = createCommands(
66
Object.entries(docProviders).map(([providerKey, providerConfig]) => ({
77
data: {
88
name: providerKey,

src/commands/guides/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ApplicationCommandOptionType, ApplicationCommandType, MessageFlags } from 'discord.js';
22
import { logToChannel } from '../../util/channel-logging.js';
3+
import { createCommand } from '../../util/commands.js';
34
import { loadMarkdownOptions } from '../../util/markdown.js';
4-
import { createCommand } from '../index.js';
55

66
const subjectsDir = new URL('./subjects/', import.meta.url);
77
const subjectChoices = new Map<string, string>();
@@ -18,7 +18,7 @@ const loadChoices = async (): Promise<void> => {
1818

1919
await loadChoices();
2020

21-
export default createCommand(
21+
export const guidesCommand = createCommand(
2222
{
2323
name: 'guides',
2424
description: 'Get a guide on a specific subject',

src/commands/index.ts

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,9 @@
1-
import type { CommandInteraction, RESTPostAPIApplicationCommandsJSONBody } from 'discord.js';
2-
import { z } from 'zod';
3-
import type { StructurePredicate } from '../util/loaders.js';
4-
5-
export type Command = {
6-
/**
7-
* The data for the command
8-
*/
9-
data: RESTPostAPIApplicationCommandsJSONBody;
10-
/**
11-
* The function execute when the command is called
12-
*
13-
* @param interaction - The interaction that triggered the command
14-
*/
15-
execute: (interaction: CommandInteraction) => Promise<void> | void;
16-
17-
__isCommand__: true;
18-
};
19-
20-
/**
21-
* Defines a schema for a command
22-
*/
23-
export const schema = z.object({
24-
data: z.custom<RESTPostAPIApplicationCommandsJSONBody>(),
25-
execute: z.function(),
26-
__isCommand__: z.literal(true),
27-
});
28-
29-
/**
30-
* Defines the predicate to check if an object is a Command
31-
*/
32-
export const predicate: StructurePredicate<Command> = (obj: unknown): obj is Command =>
33-
schema.safeParse(obj).success;
34-
35-
/**
36-
*
37-
* Creates a command object
38-
*
39-
* @param data - The command data
40-
* @param execute - The function to execute when the command is called
41-
* @returns
42-
*/
43-
export const createCommand = (
44-
data: RESTPostAPIApplicationCommandsJSONBody,
45-
execute: (interaction: CommandInteraction) => Promise<void> | void
46-
): Command => {
47-
return { data, execute, __isCommand__: true } satisfies Command;
48-
};
49-
50-
/**
51-
* Creates multiple commands
52-
*
53-
* @param commands - An array of command data and execute functions
54-
* @returns
55-
*/
56-
export const createCommands = (
57-
commands: Array<{
58-
data: RESTPostAPIApplicationCommandsJSONBody;
59-
execute: (interaction: CommandInteraction) => Promise<void> | void;
60-
}>
61-
): Command[] => {
62-
return commands.map(({ data, execute }) => createCommand(data, execute));
63-
};
1+
import { docsCommands } from './docs/index.js';
2+
import { guidesCommand } from './guides/index.js';
3+
import { pingCommand } from './ping.js';
4+
import { tipsCommands } from './tips/index.js';
5+
import type { Command } from './types.js';
6+
7+
export const commands = new Map<string, Command>(
8+
[pingCommand, guidesCommand, docsCommands, tipsCommands].flat().map((cmd) => [cmd.data.name, cmd])
9+
);

src/commands/ping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { createCommand } from './index.js';
1+
import { createCommand } from '../util/commands.js';
22

3-
export default createCommand(
3+
export const pingCommand = createCommand(
44
{
55
name: 'ping',
66
description: 'Replies with Pong!',

src/commands/tips/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ApplicationCommandOptionType, ApplicationCommandType, MessageFlags } from 'discord.js';
22
import { logToChannel } from '../../util/channel-logging.js';
3+
import { createCommand } from '../../util/commands.js';
34
import { loadMarkdownOptions } from '../../util/markdown.js';
4-
import { createCommand } from '../index.js';
55

66
const subjectsDir = new URL('./subjects/', import.meta.url);
77
const subjectChoices = new Map<string, string>();
@@ -92,4 +92,4 @@ const contextMenuCommands = Array.from(subjectChoices).map(([key, value]) =>
9292
)
9393
);
9494

95-
export default [slashCommand, ...contextMenuCommands];
95+
export const tipsCommands = [slashCommand, ...contextMenuCommands];

src/commands/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { CommandInteraction, RESTPostAPIApplicationCommandsJSONBody } from 'discord.js';
2+
3+
export type Command = {
4+
data: RESTPostAPIApplicationCommandsJSONBody;
5+
execute: (interaction: CommandInteraction) => Promise<void> | void;
6+
};

src/events/has-var.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Events } from 'discord.js';
22
import ts from 'typescript';
33
import { MINUTE } from '../constants/time.js';
4+
import { createEvent } from '../util/events.js';
45
import { codeBlockRegex } from '../util/message.js';
56
import { rateLimit } from '../util/rate-limit.js';
6-
import { createEvent } from './index.js';
77

88
const { canRun, reset } = rateLimit(5 * MINUTE);
99

@@ -37,7 +37,7 @@ const hasVarDeclaration = (code: string, language: string): boolean => {
3737
}
3838
};
3939

40-
export default createEvent(
40+
export const hasVarEvent = createEvent(
4141
{
4242
name: Events.MessageCreate,
4343
once: false,
@@ -64,7 +64,6 @@ export default createEvent(
6464
}
6565
}
6666
}
67-
6867
return;
6968
}
7069
);

src/events/index.ts

Lines changed: 12 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,12 @@
1-
import type { ClientEvents } from 'discord.js';
2-
import z from 'zod';
3-
import type { StructurePredicate } from '../util/loaders.js';
4-
5-
/**
6-
* Defines the structure of an Event
7-
*/
8-
export type DiscordEvent<T extends keyof ClientEvents = keyof ClientEvents> = {
9-
/**
10-
* The name of the event to listen to
11-
*/
12-
name: T;
13-
/**
14-
* Whether the event should be listened to only once
15-
*
16-
* @default false
17-
*/
18-
once?: boolean;
19-
/**
20-
* The function to execute when the event is triggered
21-
*
22-
* @param args - The arguments passed by the event
23-
*/
24-
execute: (...args: ClientEvents[T]) => Promise<void> | void;
25-
26-
__isEvent__: true;
27-
};
28-
29-
/**
30-
* Defines the schema for an event
31-
*/
32-
export const schema = z.object({
33-
name: z.string(),
34-
once: z.boolean().optional().default(false),
35-
execute: z.function(),
36-
__isEvent__: z.literal(true),
37-
});
38-
39-
/**
40-
* Defines the predicate to check if an object is a valid Event type.
41-
*/
42-
export const predicate: StructurePredicate<DiscordEvent> = (obj: unknown): obj is DiscordEvent =>
43-
schema.safeParse(obj).success;
44-
45-
/**
46-
*
47-
* Creates an event object
48-
*
49-
* @param data - The event data
50-
* @param execute - The function to execute when the event is triggered
51-
* @returns
52-
*/
53-
export const createEvent = <T extends keyof ClientEvents = keyof ClientEvents>(
54-
data: {
55-
name: T;
56-
once?: boolean;
57-
},
58-
execute: (...args: ClientEvents[T]) => Promise<void> | void
59-
): DiscordEvent<T> => {
60-
return { ...data, execute, __isEvent__: true } satisfies DiscordEvent<T>;
61-
};
62-
63-
/**
64-
*
65-
* Creates multiple events
66-
*
67-
* @param events - An array of event data and execute functions
68-
* @returns
69-
*/
70-
export const createEvents = <T extends keyof ClientEvents = keyof ClientEvents>(
71-
events: Array<{
72-
data: {
73-
name: T;
74-
once?: boolean;
75-
};
76-
execute: (...args: ClientEvents[T]) => Promise<void> | void;
77-
}>
78-
): DiscordEvent<T>[] => {
79-
return events.map(({ data, execute }) => createEvent(data, execute));
80-
};
1+
import { hasVarEvent } from './has-var.js';
2+
import { interactionCreateEvent } from './interaction-create.js';
3+
import { justAskEvent } from './just-ask.js';
4+
import { readyEvent } from './ready.js';
5+
import type { DiscordEvent } from './types.js';
6+
7+
export const events = [
8+
readyEvent,
9+
justAskEvent,
10+
hasVarEvent,
11+
interactionCreateEvent,
12+
] as DiscordEvent[];

0 commit comments

Comments
 (0)