meocord 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +152 -140
  3. package/dist/cjs/_shared/meocord.app-CHjdCAA_.cjs +496 -0
  4. package/dist/cjs/_shared/theme-BdtbtMZX.cjs +176 -0
  5. package/dist/cjs/common/index.cjs +16 -0
  6. package/dist/cjs/core/index.cjs +35 -0
  7. package/dist/cjs/decorator/index.cjs +360 -0
  8. package/dist/cjs/enum/index.cjs +20 -0
  9. package/dist/cjs/interface/index.cjs +2 -0
  10. package/dist/esm/bin/generator.js +92 -0
  11. package/dist/esm/bin/helper/controller-generator.helper.js +105 -0
  12. package/dist/esm/bin/helper/guard-generator.helper.js +33 -0
  13. package/dist/esm/bin/helper/service-generator.helper.js +33 -0
  14. package/dist/esm/bin/meocord.js +333 -0
  15. package/dist/esm/common/index.js +2 -0
  16. package/dist/esm/common/logger.js +72 -0
  17. package/dist/{core/index.d.ts → esm/common/theme.js} +8 -2
  18. package/dist/esm/core/index.js +1 -0
  19. package/dist/esm/core/meocord-factory.js +28 -0
  20. package/dist/esm/core/meocord.app.js +267 -0
  21. package/dist/esm/decorator/app.decorator.js +99 -0
  22. package/dist/esm/decorator/command-builder.decorator.js +32 -0
  23. package/dist/esm/decorator/container.js +6 -0
  24. package/dist/esm/decorator/controller.decorator.js +218 -0
  25. package/dist/esm/decorator/guard.decorator.js +165 -0
  26. package/dist/esm/decorator/index.js +6 -0
  27. package/dist/esm/decorator/service.decorator.js +58 -0
  28. package/dist/esm/enum/controller.enum.js +43 -0
  29. package/dist/esm/enum/index.js +1 -0
  30. package/dist/esm/interface/index.js +1 -0
  31. package/dist/esm/package.json.js +5 -0
  32. package/dist/esm/util/common.util.js +68 -0
  33. package/dist/esm/util/embed.util.js +13 -0
  34. package/dist/esm/util/generator-cli.util.js +107 -0
  35. package/dist/{util → esm/util}/json.util.js +10 -6
  36. package/dist/esm/util/meocord-cli.util.js +172 -0
  37. package/dist/esm/util/meocord-config-loader.util.js +48 -0
  38. package/dist/esm/util/tsconfig.util.js +83 -0
  39. package/dist/{util → esm/util}/wait.util.js +5 -1
  40. package/dist/{common/logger.d.ts → types/common/index.d.ts} +30 -1
  41. package/dist/{core/meocord.app.d.ts → types/core/index.d.ts} +30 -2
  42. package/dist/types/decorator/index.d.ts +425 -0
  43. package/dist/types/enum/index.d.ts +18 -0
  44. package/dist/{interface → types/interface}/index.d.ts +11 -7
  45. package/package.json +64 -48
  46. package/webpack.config.js +2 -2
  47. package/dist/bin/generator.d.ts +0 -29
  48. package/dist/bin/generator.js +0 -17
  49. package/dist/bin/helper/controller-generator.helper.d.ts +0 -67
  50. package/dist/bin/helper/controller-generator.helper.js +0 -50
  51. package/dist/bin/helper/guard-generator.helper.d.ts +0 -32
  52. package/dist/bin/helper/guard-generator.helper.js +0 -25
  53. package/dist/bin/helper/service-generator.helper.d.ts +0 -32
  54. package/dist/bin/helper/service-generator.helper.js +0 -25
  55. package/dist/bin/meocord.d.ts +0 -19
  56. package/dist/bin/meocord.js +0 -34
  57. package/dist/common/index.d.ts +0 -19
  58. package/dist/common/index.js +0 -17
  59. package/dist/common/logger.js +0 -17
  60. package/dist/common/theme.d.ts +0 -24
  61. package/dist/common/theme.js +0 -17
  62. package/dist/core/index.js +0 -17
  63. package/dist/core/meocord-factory.d.ts +0 -24
  64. package/dist/core/meocord-factory.js +0 -17
  65. package/dist/core/meocord.app.js +0 -17
  66. package/dist/decorator/app.decorator.d.ts +0 -59
  67. package/dist/decorator/app.decorator.js +0 -61
  68. package/dist/decorator/command-builder.decorator.d.ts +0 -39
  69. package/dist/decorator/command-builder.decorator.js +0 -35
  70. package/dist/decorator/container.d.ts +0 -20
  71. package/dist/decorator/container.js +0 -17
  72. package/dist/decorator/controller.decorator.d.ts +0 -125
  73. package/dist/decorator/controller.decorator.js +0 -113
  74. package/dist/decorator/guard.decorator.d.ts +0 -101
  75. package/dist/decorator/guard.decorator.js +0 -94
  76. package/dist/decorator/index.d.ts +0 -23
  77. package/dist/decorator/index.js +0 -17
  78. package/dist/decorator/service.decorator.d.ts +0 -36
  79. package/dist/decorator/service.decorator.js +0 -36
  80. package/dist/enum/controller.enum.d.ts +0 -42
  81. package/dist/enum/controller.enum.js +0 -19
  82. package/dist/enum/index.d.ts +0 -18
  83. package/dist/enum/index.js +0 -17
  84. package/dist/interface/command-decorator.interface.d.ts +0 -43
  85. package/dist/interface/command-decorator.interface.js +0 -1
  86. package/dist/interface/index.js +0 -1
  87. package/dist/util/common.util.d.ts +0 -40
  88. package/dist/util/common.util.js +0 -38
  89. package/dist/util/embed.util.d.ts +0 -19
  90. package/dist/util/embed.util.js +0 -17
  91. package/dist/util/generator-cli.util.d.ts +0 -65
  92. package/dist/util/generator-cli.util.js +0 -49
  93. package/dist/util/index.d.ts +0 -18
  94. package/dist/util/index.js +0 -17
  95. package/dist/util/json.util.d.ts +0 -27
  96. package/dist/util/meocord-cli.util.d.ts +0 -62
  97. package/dist/util/meocord-cli.util.js +0 -50
  98. package/dist/util/meocord-config-loader.util.d.ts +0 -32
  99. package/dist/util/meocord-config-loader.util.js +0 -34
  100. package/dist/util/tsconfig.util.d.ts +0 -29
  101. package/dist/util/tsconfig.util.js +0 -32
  102. package/dist/util/wait.util.d.ts +0 -18
  103. /package/dist/{bin → esm/bin}/builder-template/builder/context-menu.builder.template +0 -0
  104. /package/dist/{bin → esm/bin}/builder-template/builder/slash.builder.template +0 -0
  105. /package/dist/{bin → esm/bin}/builder-template/controller/button.controller.template +0 -0
  106. /package/dist/{bin → esm/bin}/builder-template/controller/context-menu.controller.template +0 -0
  107. /package/dist/{bin → esm/bin}/builder-template/controller/message.controller.template +0 -0
  108. /package/dist/{bin → esm/bin}/builder-template/controller/modal-submit.controller.template +0 -0
  109. /package/dist/{bin → esm/bin}/builder-template/controller/reaction.controller.template +0 -0
  110. /package/dist/{bin → esm/bin}/builder-template/controller/select-menu.controller.template +0 -0
  111. /package/dist/{bin → esm/bin}/builder-template/controller/slash.controller.template +0 -0
  112. /package/dist/{bin → esm/bin}/builder-template/guard.template +0 -0
  113. /package/dist/{bin → esm/bin}/builder-template/service.template +0 -0
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ require('reflect-metadata');
4
+ var theme = require('../_shared/theme-BdtbtMZX.cjs');
5
+ var meocord_app = require('../_shared/meocord.app-CHjdCAA_.cjs');
6
+ require('inversify');
7
+ require('discord.js');
8
+ require('path');
9
+ require('fs');
10
+ require('jiti');
11
+ require('node:util');
12
+ require('dayjs');
13
+ require('dayjs/plugin/utc.js');
14
+ require('dayjs/plugin/timezone.js');
15
+ require('chalk');
16
+ require('../enum/index.cjs');
17
+ require('lodash-es');
18
+
19
+ class MeoCordFactory {
20
+ static create(target) {
21
+ const container = Reflect.getMetadata('inversify:container', target);
22
+ if (!container) {
23
+ if (typeof target === 'function') {
24
+ this.logger.error(`No container found for class: ${target.name}`);
25
+ } else {
26
+ this.logger.error('No container found for the provided target.');
27
+ }
28
+ throw new Error('No container found on the target class.');
29
+ }
30
+ return meocord_app.mainContainer.get(meocord_app.MeoCordApp);
31
+ }
32
+ }
33
+ MeoCordFactory.logger = new theme.Logger();
34
+
35
+ exports.MeoCordFactory = MeoCordFactory;
@@ -0,0 +1,360 @@
1
+ 'use strict';
2
+
3
+ require('reflect-metadata');
4
+ var meocord_app = require('../_shared/meocord.app-CHjdCAA_.cjs');
5
+ var inversify = require('inversify');
6
+ var discord_js = require('discord.js');
7
+ var theme = require('../_shared/theme-BdtbtMZX.cjs');
8
+ require('../enum/index.cjs');
9
+ require('lodash-es');
10
+ require('node:util');
11
+ require('dayjs');
12
+ require('dayjs/plugin/utc.js');
13
+ require('dayjs/plugin/timezone.js');
14
+ require('path');
15
+ require('fs');
16
+ require('jiti');
17
+ require('chalk');
18
+
19
+ /**
20
+ * `@Service()` decorator to mark a class as a service that can be injected into controllers or used as standalone services.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * @Service()
25
+ * class MyService {
26
+ * constructor(private anotherService: AnotherService) {}
27
+ *
28
+ * doSomething() {
29
+ * this.anotherService.alsoDoSomething()
30
+ * console.log('Hello, World!')
31
+ * }
32
+ * }
33
+ * ```
34
+ * @returns A decorator function to apply to the class.
35
+ */ function Service() {
36
+ return function(target) {
37
+ // Check if the class is already injectable; if not, make it injectable dynamically
38
+ if (!Reflect.hasMetadata('inversify:injectable', target)) {
39
+ inversify.injectable()(target);
40
+ }
41
+ // Check if the class is already injectable; if not, make it injectable dynamically
42
+ if (!meocord_app.mainContainer.isBound(target)) {
43
+ // Bind the target class to the container in a singleton scope
44
+ meocord_app.mainContainer.bind(target).toSelf().inSingletonScope();
45
+ }
46
+ // Recursively bind dependencies
47
+ bindDependencies$1(target);
48
+ };
49
+ }
50
+ function bindDependencies$1(target) {
51
+ // Get the constructor parameter types using Reflect metadata
52
+ const dependencies = Reflect.getMetadata('design:paramtypes', target) || [];
53
+ dependencies.forEach((dep)=>{
54
+ // Bind the dependency if not already bound
55
+ if (!meocord_app.mainContainer.isBound(dep)) {
56
+ if (dep.name === discord_js.Client.name) return;
57
+ try {
58
+ // Check if the class is already injectable; if not, make it injectable dynamically
59
+ if (!Reflect.hasMetadata('inversify:injectable', dep)) {
60
+ inversify.injectable()(dep);
61
+ }
62
+ meocord_app.mainContainer.bind(dep).toSelf().inSingletonScope();
63
+ bindDependencies$1(dep); // Recur for the dependencies of the current dependency
64
+ } catch (error) {
65
+ console.warn(`Could not bind dependency: ${dep?.name || dep}`, error);
66
+ }
67
+ }
68
+ });
69
+ }
70
+
71
+ /**
72
+ * This decorator is used to mark a class as a Discord command builder that later can be registered on the `@Command` decorator.
73
+ * It defines the command type using metadata and dynamically makes the class injectable if it isn't already.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * @CommandBuilder(CommandType.SLASH)
78
+ * export class MySlashCommand implements CommandBuilderBase {
79
+ * build(commandName: string): SlashCommandBuilder {
80
+ * return new SlashCommandBuilder().setName(commandName).setDescription('A sample slash command')
81
+ * }
82
+ * }
83
+ *```
84
+ *
85
+ * @param commandType - The type of the command, specified from the `CommandType` enum.
86
+ * @returns A decorator function that makes the target class injectable
87
+ * and assigns the `commandType` metadata.
88
+ */ function CommandBuilder(commandType) {
89
+ return function(target) {
90
+ // Check if the class is already injectable; if not, make it injectable dynamically
91
+ if (!Reflect.hasMetadata('inversify:injectable', target)) {
92
+ inversify.injectable()(target);
93
+ }
94
+ // Define the command type metadata for the target class
95
+ Reflect.defineMetadata('commandType', commandType, target);
96
+ };
97
+ }
98
+
99
+ function isValidContext(context) {
100
+ return context instanceof discord_js.BaseInteraction || context instanceof discord_js.Message || context instanceof discord_js.MessageReaction;
101
+ }
102
+ function applyGuards(descriptor, guards, propertyKey) {
103
+ const originalMethod = descriptor.value;
104
+ descriptor.value = async function(...args) {
105
+ const [context] = args;
106
+ if (!isValidContext(context)) {
107
+ throw new Error(`The first argument of ${String(propertyKey)} must be an instance of Interaction, Message, or MessageReaction.`);
108
+ }
109
+ // Iterate over each guard and check if it allows the method to proceed
110
+ for (const guard of guards){
111
+ let guardInstance;
112
+ if (isGuardWithParams(guard)) {
113
+ const { provide, params } = guard;
114
+ guardInstance = meocord_app.mainContainer.get(provide, {
115
+ autobind: true
116
+ });
117
+ // Inject the parameters into the guard instance
118
+ Object.assign(guardInstance, params);
119
+ } else {
120
+ // Resolve guard without parameters
121
+ guardInstance = meocord_app.mainContainer.get(guard, {
122
+ autobind: true
123
+ });
124
+ }
125
+ // Ensure the guard has the necessary method `canActivate`
126
+ if (!guardInstance.canActivate) {
127
+ throw new Error(`Guard ${guard.constructor.name} applied to ${String(propertyKey)} does not have a valid canActivate method.`);
128
+ }
129
+ // Check if the guard allows the method to proceed
130
+ const canActivate = await guardInstance.canActivate(...args);
131
+ if (!canActivate) {
132
+ return; // Prevent method execution if the guard fails
133
+ }
134
+ }
135
+ // Call the original method if all guards pass
136
+ return originalMethod.apply(this, args);
137
+ };
138
+ }
139
+ /**
140
+ * `@Guard()` decorator to mark a class as a Guard that later can be added on `@UseGuard` decorator.
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * @Guard()
145
+ export class ButtonInteractionGuard implements GuardInterface {
146
+ private readonly logger = new Logger(ButtonInteractionGuard.name)
147
+
148
+ async canActivate(context: ButtonInteraction, { ownerId }: { ownerId: string }): Promise<boolean> {
149
+ if (context.user.id !== ownerId) {
150
+ this.logger.error(
151
+ `User with id ${context.user.id} is not allowed to use this command that initiated by user with id ${ownerId}.`,
152
+ )
153
+ const embed = generateErrorEmbed(
154
+ `Hi <@${context.user.id}>, this command can only be used by the person who initiated it: <@${ownerId}>.`,
155
+ )
156
+ await context.reply({
157
+ embeds: [embed],
158
+ flags: MessageFlagsBitField.Flags.Ephemeral,
159
+ })
160
+ return false
161
+ }
162
+ return true
163
+ }
164
+ }
165
+ * ```
166
+ */ function Guard() {
167
+ return function(target) {
168
+ // Check if the class is already injectable; if not, make it injectable dynamically
169
+ if (!Reflect.hasMetadata('inversify:injectable', target)) {
170
+ inversify.injectable()(target);
171
+ }
172
+ meocord_app.mainContainer.bind(target).toSelf().inTransientScope();
173
+ // Bind any dependencies that the guard requires
174
+ const injectables = Reflect.getMetadata('design:paramtypes', target) || [];
175
+ injectables.forEach((dep)=>{
176
+ if (!meocord_app.mainContainer.isBound(dep)) {
177
+ meocord_app.mainContainer.bind(dep).toSelf().inSingletonScope();
178
+ }
179
+ });
180
+ };
181
+ }
182
+ /**
183
+ * Type guard to check if the object is a GuardWithParams.
184
+ * This function helps to check whether a guard is parameterized or not.
185
+ *
186
+ * @param guard - The guard to check.
187
+ * @returns `true` if the guard has parameters, otherwise `false`.
188
+ */ function isGuardWithParams(guard) {
189
+ return typeof guard === 'object' && 'provide' in guard && 'params' in guard;
190
+ }
191
+ /**
192
+ * `@UseGuard()` decorator to apply one or more guards to methods.
193
+ * Guards are used to handle permission checks before executing a method.
194
+ * Each guard must use `@Guard` decorator and implement the `canActivate` method, which determines
195
+ * whether the method should be allowed to execute based on the provided context (Interaction, Message, or Reaction) and arguments.
196
+ * This decorator ensures that all guards pass validation before calling the original method.
197
+ * Supports guards that are parameterized (accepting additional parameters).
198
+ *
199
+ * @param guards - One or more guard classes to apply. These can be regular guards or guards with additional parameters.
200
+ * - If providing a guard with parameters, it should be an object with:
201
+ * - `provide`: The guard class to instantiate. Must implement `GuardInterface`.
202
+ * - `params`: A record of key-value pairs to be passed as additional properties to the guard instance.
203
+ * @returns A method decorator function that applies the guards to the method.
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * // Method-level usage
208
+ * @Command('profile-{id}', CommandType.BUTTON)
209
+ * @UseGuard(
210
+ * { provide: RateLimiterGuard, params: { limit: 2, window: 3000 } },
211
+ * ButtonInteractionGuard
212
+ * )
213
+ * async showProfileById(interaction: ButtonInteraction, { id }: { id: string }) {
214
+ * await interaction.reply(`Profile ID: ${id}`)
215
+ * }
216
+ *
217
+ * // Class-level usage
218
+ * @Controller()
219
+ * @UseGuard(GlobalGuard)
220
+ * class MyController {
221
+ * @Command('ping', CommandType.SLASH)
222
+ * async ping(interaction: ChatInputCommandInteraction) {
223
+ * await interaction.reply('Pong!')
224
+ * }
225
+ * }
226
+ * ```
227
+ */ function UseGuard(...guards) {
228
+ return function(target, propertyKey, descriptor) {
229
+ if (descriptor && propertyKey) {
230
+ // Method Decorator
231
+ applyGuards(descriptor, guards, String(propertyKey));
232
+ // Store guard metadata for later access (if needed)
233
+ Reflect.defineMetadata('guards', guards, target, propertyKey);
234
+ } else if (typeof target === 'function' && !propertyKey && !descriptor) {
235
+ // Class Decorator
236
+ const prototype = target.prototype;
237
+ // 1. Get all methods to guard
238
+ const methods = new Set();
239
+ const commandMap = meocord_app.getCommandMap(prototype) || {};
240
+ Object.values(commandMap).flat().forEach((cmd)=>methods.add(cmd.methodName));
241
+ const messageHandlers = meocord_app.getMessageHandlers(prototype) || [];
242
+ messageHandlers.forEach((handler)=>methods.add(handler.method));
243
+ const reactionHandlers = meocord_app.getReactionHandlers(prototype) || [];
244
+ reactionHandlers.forEach((handler)=>methods.add(handler.method));
245
+ for (const methodName of methods){
246
+ const methodDescriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
247
+ if (methodDescriptor) {
248
+ applyGuards(methodDescriptor, guards, methodName);
249
+ Object.defineProperty(prototype, methodName, methodDescriptor);
250
+ Reflect.defineMetadata('guards', guards, prototype, methodName);
251
+ }
252
+ }
253
+ }
254
+ };
255
+ }
256
+
257
+ /**
258
+ * Binds a class and its dependencies to the Inversify container in singleton scope.
259
+ *
260
+ * @param {Container} container - The Inversify container instance.
261
+ * @param {any} cls - The class to be bound to the container.
262
+ */ function bindDependencies(container, cls) {
263
+ if (!container.isBound(cls)) {
264
+ container.bind(cls).toSelf().inSingletonScope();
265
+ const dependencies = Reflect.getMetadata('design:paramtypes', cls) || [];
266
+ dependencies.forEach((dep)=>bindDependencies(container, dep));
267
+ }
268
+ }
269
+ /**
270
+ * Resolves dependencies for a given class by binding them to the container and returning the resolved instances.
271
+ *
272
+ * @param {Container} container - The Inversify container instance.
273
+ * @param {any} target - The target class whose dependencies are to be resolved.
274
+ * @returns {any[]} - An array of resolved instances of the target's dependencies.
275
+ */ function resolveDependencies(container, target) {
276
+ const injectables = Reflect.getMetadata('design:paramtypes', target) || [];
277
+ return injectables.map((dep)=>{
278
+ bindDependencies(container, dep);
279
+ return container.get(dep);
280
+ });
281
+ }
282
+ /**
283
+ * `@MeoCord()` decorator for initializing and setting up the MeoCord application.
284
+ *
285
+ * @param {Object} options - The decorator options.
286
+ * @param {ServiceIdentifier[]} options.controllers - The list of controllers to be registered.
287
+ * @param {ClientOptions} options.clientOptions - The Discord client options for initializing the bot.
288
+ * @param {ActivityOptions[]} [options.activities] - Optional activities for the bot.
289
+ * @param {ServiceIdentifier[]} [options.services] - Optional services to be registered.
290
+ *
291
+ * @example
292
+ * ```typescript
293
+ * @MeoCord({
294
+ * controllers: [PingSlashController],
295
+ * clientOptions: {
296
+ * intents: [
297
+ * GatewayIntentBits.Guilds,
298
+ * GatewayIntentBits.GuildMembers,
299
+ * GatewayIntentBits.GuildMessages,
300
+ * GatewayIntentBits.GuildMessageReactions,
301
+ * GatewayIntentBits.MessageContent,
302
+ * ],
303
+ * partials: [Partials.Message, Partials.Channel, Partials.Reaction],
304
+ * },
305
+ * activities: [{
306
+ * name: `${sample(['Genshin', 'ZZZ'])} with Romeo`,
307
+ * type: ActivityType.Playing,
308
+ * url: 'https://enka.network/u/824957678/',
309
+ * }],
310
+ * services: [MyStandaloneService],
311
+ * })
312
+ * class MyApp {}
313
+ * ```
314
+ **/ function MeoCord(options) {
315
+ return (target)=>{
316
+ if (!Reflect.hasMetadata('inversify:injectable', target)) {
317
+ inversify.injectable()(target); // Make target injectable (inversify-specific)
318
+ }
319
+ const meocordConfig = theme.loadMeoCordConfig();
320
+ if (!meocordConfig) return;
321
+ const discordClient = new discord_js.Client(options.clientOptions);
322
+ meocord_app.mainContainer.bind(discord_js.Client).toConstantValue(discordClient);
323
+ [
324
+ ...options.controllers,
325
+ ...options.services || []
326
+ ].forEach((dep)=>{
327
+ bindDependencies(meocord_app.mainContainer, dep);
328
+ });
329
+ // Bind other static values to the container
330
+ meocord_app.mainContainer.bind(target).toConstantValue(options.clientOptions);
331
+ if (options.activities) {
332
+ meocord_app.mainContainer.bind(target).toConstantValue(options.activities);
333
+ }
334
+ if (options.services) {
335
+ meocord_app.mainContainer.bind(target).toConstantValue(options.services.map((s)=>meocord_app.mainContainer.get(s)));
336
+ }
337
+ const meocordApp = new meocord_app.MeoCordApp(options.controllers.map((c)=>meocord_app.mainContainer.get(c)), discordClient, meocordConfig.discordToken, options?.activities);
338
+ meocord_app.mainContainer.bind(meocord_app.MeoCordApp).toConstantValue(meocordApp);
339
+ // Bind the App class dynamically with resolved dependencies
340
+ meocord_app.mainContainer.bind(target).toDynamicValue(()=>{
341
+ const dependencies = resolveDependencies(meocord_app.mainContainer, target);
342
+ return new target(...dependencies);
343
+ }).inSingletonScope();
344
+ Reflect.defineMetadata('inversify:container', meocord_app.mainContainer, target);
345
+ };
346
+ }
347
+
348
+ exports.Command = meocord_app.Command;
349
+ exports.Controller = meocord_app.Controller;
350
+ exports.MessageHandler = meocord_app.MessageHandler;
351
+ exports.ReactionHandler = meocord_app.ReactionHandler;
352
+ exports.getCommandMap = meocord_app.getCommandMap;
353
+ exports.getMessageHandlers = meocord_app.getMessageHandlers;
354
+ exports.getReactionHandlers = meocord_app.getReactionHandlers;
355
+ exports.mainContainer = meocord_app.mainContainer;
356
+ exports.CommandBuilder = CommandBuilder;
357
+ exports.Guard = Guard;
358
+ exports.MeoCord = MeoCord;
359
+ exports.Service = Service;
360
+ exports.UseGuard = UseGuard;
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var CommandType = /*#__PURE__*/ function(CommandType) {
4
+ CommandType["SLASH"] = "SLASH";
5
+ CommandType["BUTTON"] = "BUTTON";
6
+ CommandType["CONTEXT_MENU"] = "CONTEXT_MENU";
7
+ CommandType["SELECT_MENU"] = "SELECT_MENU";
8
+ CommandType["MODAL_SUBMIT"] = "MODAL_SUBMIT";
9
+ return CommandType;
10
+ }({});
11
+ /**
12
+ * Enum representing actions that can be performed on a message reaction.
13
+ */ var ReactionHandlerAction = /*#__PURE__*/ function(ReactionHandlerAction) {
14
+ /** Reaction added to a message. */ ReactionHandlerAction["ADD"] = "ADD";
15
+ /** Reaction removed from a message. */ ReactionHandlerAction["REMOVE"] = "REMOVE";
16
+ return ReactionHandlerAction;
17
+ }({});
18
+
19
+ exports.CommandType = CommandType;
20
+ exports.ReactionHandlerAction = ReactionHandlerAction;
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+
@@ -0,0 +1,92 @@
1
+ import { Argument } from 'commander';
2
+ import { ControllerGeneratorHelper } from './helper/controller-generator.helper.js';
3
+ import { Logger } from '../common/logger.js';
4
+ import '../common/theme.js';
5
+ import { ServiceGeneratorHelper } from './helper/service-generator.helper.js';
6
+ import { GuardGeneratorHelper } from './helper/guard-generator.helper.js';
7
+ import wait from '../util/wait.util.js';
8
+
9
+ class GeneratorCLI {
10
+ register(program) {
11
+ const generatorCommand = program.command('generate').alias('g').description('Generate components');
12
+ generatorCommand.command('controller').alias('co').description('Generate a controller component').addArgument(new Argument('<type>', 'Type of the controller (e.g., button, context-menu, etc.)').choices([
13
+ 'button',
14
+ 'context-menu',
15
+ 'message',
16
+ 'modal-submit',
17
+ 'reaction',
18
+ 'select-menu',
19
+ 'slash'
20
+ ])).addArgument(new Argument('<name>', 'Name of the controller')).action(async (type, name)=>{
21
+ await this.handleGenerateComponent({
22
+ component: 'controller',
23
+ type: type,
24
+ name
25
+ });
26
+ });
27
+ generatorCommand.command('service').alias('s').addArgument(new Argument('<name>', 'Name of the service.')).description('Generate a service component').action(async (name)=>{
28
+ await this.handleGenerateComponent({
29
+ component: 'service',
30
+ name
31
+ });
32
+ });
33
+ generatorCommand.command('guard').alias('gu').addArgument(new Argument('<name>', 'Name of the guard.')).description('Generate a guard component').action(async (name)=>{
34
+ await this.handleGenerateComponent({
35
+ component: 'guard',
36
+ name
37
+ });
38
+ });
39
+ return program;
40
+ }
41
+ async handleGenerateComponent(args) {
42
+ const { component, name, type } = args;
43
+ if (!name) {
44
+ this.logger.error('Name is required');
45
+ await wait(100);
46
+ process.exit(1);
47
+ }
48
+ switch(component){
49
+ case 'controller':
50
+ if (!type) {
51
+ this.logger.error('Type is required for controllers');
52
+ await wait(100);
53
+ process.exit(1);
54
+ }
55
+ await this.handleGenerateController({
56
+ name,
57
+ type
58
+ });
59
+ break;
60
+ case 'service':
61
+ this.serviceGeneratorHelper.generateService(name);
62
+ break;
63
+ case 'guard':
64
+ this.guardGeneratorHelper.generateGuard(name);
65
+ break;
66
+ default:
67
+ this.logger.error(`Unsupported component type: ${component}`);
68
+ await wait(100);
69
+ process.exit(1);
70
+ }
71
+ }
72
+ async handleGenerateController(args) {
73
+ try {
74
+ this.controllerGeneratorHelper.generateController({
75
+ controllerName: args.name
76
+ }, args.type);
77
+ } catch (error) {
78
+ this.logger.error(`Error generating controller: ${error instanceof Error ? error.message : String(error)}`);
79
+ await wait(100);
80
+ process.exit(1);
81
+ }
82
+ }
83
+ constructor(appName){
84
+ this.appName = appName;
85
+ this.logger = new Logger(this.appName);
86
+ this.controllerGeneratorHelper = new ControllerGeneratorHelper();
87
+ this.serviceGeneratorHelper = new ServiceGeneratorHelper(this.appName);
88
+ this.guardGeneratorHelper = new GuardGeneratorHelper(this.appName);
89
+ }
90
+ }
91
+
92
+ export { GeneratorCLI };
@@ -0,0 +1,105 @@
1
+ import path from 'path';
2
+ import { ControllerType } from '../../enum/controller.enum.js';
3
+ import { validateAndFormatName, populateTemplate, createDirectoryIfNotExists, generateFile } from '../../util/generator-cli.util.js';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename$1 = fileURLToPath(import.meta.url);
7
+ const __dirname$1 = path.dirname(__filename$1);
8
+ class ControllerGeneratorHelper {
9
+ /**
10
+ * Generates a new controller file and an associated structure based on the provided arguments and controller type.
11
+ * @param args - The arguments for generating the controller, including the optional controller name.
12
+ * @param type - The type of the controller to generate, defined in the `ControllerType` enum.
13
+ * @throws Will throw an error if the controller name is invalid or if the controller type is unsupported.
14
+ */ generateController(args, type) {
15
+ const { parts, kebabCaseName, className } = validateAndFormatName(args.controllerName);
16
+ const controllerDir = path.join(process.cwd(), 'src', 'controllers', type, ...parts);
17
+ const template = this.buildControllerTemplate(className, type);
18
+ this.generateControllerStructure(controllerDir, kebabCaseName, className, type, template);
19
+ }
20
+ /**
21
+ * Builds the controller template content by populating a template with variables.
22
+ * @param className - The name of the controller class.
23
+ * @param type - The type of the controller, defined in the `ControllerType` enum.
24
+ * @returns The populated template string for the controller.
25
+ * @throws Will throw an error if the controller type is unsupported.
26
+ */ buildControllerTemplate(className, type) {
27
+ const templateConfig = this.getTemplateConfig(type, className);
28
+ if (!templateConfig) {
29
+ throw new Error(`Unsupported controller type: ${type}`);
30
+ }
31
+ return populateTemplate(templateConfig.template, templateConfig.variables);
32
+ }
33
+ /**
34
+ * Retrieves the template configuration for a specific controller type and class name.
35
+ * @param type - The type of the controller, defined in the `ControllerType` enum.
36
+ * @param className - The name of the controller class.
37
+ * @returns An object containing the template path and variables, or `undefined` if not found.
38
+ */ getTemplateConfig(type, className) {
39
+ const baseDir = path.resolve(__dirname$1, '..', 'builder-template', 'controller');
40
+ const templates = {
41
+ [ControllerType.BUTTON]: 'button.controller.template',
42
+ [ControllerType.MODAL_SUBMIT]: 'modal-submit.controller.template',
43
+ [ControllerType.SELECT_MENU]: 'select-menu.controller.template',
44
+ [ControllerType.REACTION]: 'reaction.controller.template',
45
+ [ControllerType.MESSAGE]: 'message.controller.template',
46
+ [ControllerType.CONTEXT_MENU]: 'context-menu.controller.template',
47
+ [ControllerType.SLASH]: 'slash.controller.template'
48
+ };
49
+ const template = templates[type] ? path.resolve(baseDir, templates[type]) : undefined;
50
+ return template ? {
51
+ template,
52
+ variables: {
53
+ className
54
+ }
55
+ } : undefined;
56
+ }
57
+ /**
58
+ * Generates the controller file and its associated structure (e.g., builder files, directories).
59
+ * @param controllerDir - The absolute path to the controller directory.
60
+ * @param kebabCaseName - The kebab-case name of the controller file.
61
+ * @param className - The name of the controller class.
62
+ * @param type - The type of the controller, defined in the `ControllerType` enum.
63
+ * @param controllerTemplate - The populated template string for the controller file.
64
+ */ generateControllerStructure(controllerDir, kebabCaseName, className, type, controllerTemplate) {
65
+ this.generateBuilderFile(className, type, controllerDir);
66
+ createDirectoryIfNotExists(controllerDir);
67
+ const controllerFilePath = path.join(controllerDir, `${kebabCaseName}.${type}.controller.ts`);
68
+ generateFile(controllerFilePath, controllerTemplate);
69
+ }
70
+ /**
71
+ * Generates a builder file for the specified controller type and stores it in the controller directory.
72
+ * @param className - The name of the controller class.
73
+ * @param type - The type of the controller, defined in the `ControllerType` enum.
74
+ * @param controllerDir - The absolute path to the controller directory.
75
+ */ generateBuilderFile(className, type, controllerDir) {
76
+ const builderConfig = this.getBuilderConfig(type, className);
77
+ if (!builderConfig) return;
78
+ const builderTemplate = populateTemplate(builderConfig.template, builderConfig.variables);
79
+ const buildersDir = path.join(controllerDir, 'builders');
80
+ createDirectoryIfNotExists(buildersDir);
81
+ const builderFilePath = path.join(buildersDir, 'sample.builder.ts');
82
+ generateFile(builderFilePath, builderTemplate);
83
+ }
84
+ /**
85
+ * Retrieves the configuration for generating a builder file based on the controller type and class name.
86
+ * @param type - The type of the controller, defined in the `ControllerType` enum.
87
+ * @param className - The name of the controller class.
88
+ * @returns An object containing the builder template path and variables, or `undefined` if not found.
89
+ */ getBuilderConfig(type, className) {
90
+ const baseDir = path.resolve(__dirname$1, '..', 'builder-template', 'builder');
91
+ const templates = {
92
+ [ControllerType.CONTEXT_MENU]: 'context-menu.builder.template',
93
+ [ControllerType.SLASH]: 'slash.builder.template'
94
+ };
95
+ const template = templates[type] ? path.resolve(baseDir, templates[type]) : undefined;
96
+ return template ? {
97
+ template,
98
+ variables: {
99
+ className
100
+ }
101
+ } : undefined;
102
+ }
103
+ }
104
+
105
+ export { ControllerGeneratorHelper };
@@ -0,0 +1,33 @@
1
+ import path from 'path';
2
+ import { Logger } from '../../common/logger.js';
3
+ import '../../common/theme.js';
4
+ import { validateAndFormatName, buildTemplate, createDirectoryIfNotExists, generateFile } from '../../util/generator-cli.util.js';
5
+
6
+ class GuardGeneratorHelper {
7
+ /**
8
+ * Generates a guard file based on the provided guard name.
9
+ * Validates and formats the guard name, creates the necessary directories,
10
+ * and generates the guard file using a predefined template.
11
+ *
12
+ * @param guardName - The name of the guard to generate.
13
+ * It can include slashes for nested paths.
14
+ * @throws Exits the process if the guard name is not provided or invalid.
15
+ */ generateGuard(guardName) {
16
+ if (!guardName) {
17
+ this.logger.error('Guard name is required.');
18
+ process.exit(1);
19
+ }
20
+ const { parts, kebabCaseName, className } = validateAndFormatName(guardName);
21
+ const guardDir = path.join(process.cwd(), 'src', 'guards', ...parts);
22
+ const guardFile = path.join(guardDir, `${kebabCaseName}.guard.ts`);
23
+ const guardTemplate = buildTemplate(className, 'guard.template');
24
+ createDirectoryIfNotExists(guardDir);
25
+ generateFile(guardFile, guardTemplate);
26
+ }
27
+ constructor(appName){
28
+ this.appName = appName;
29
+ this.logger = new Logger(this.appName);
30
+ }
31
+ }
32
+
33
+ export { GuardGeneratorHelper };