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,165 @@
1
+ import 'reflect-metadata';
2
+ import { injectable } from 'inversify';
3
+ import { mainContainer } from './container.js';
4
+ import { BaseInteraction, Message, MessageReaction } from 'discord.js';
5
+ import { getCommandMap, getMessageHandlers, getReactionHandlers } from './controller.decorator.js';
6
+
7
+ function isValidContext(context) {
8
+ return context instanceof BaseInteraction || context instanceof Message || context instanceof MessageReaction;
9
+ }
10
+ function applyGuards(descriptor, guards, propertyKey) {
11
+ const originalMethod = descriptor.value;
12
+ descriptor.value = async function(...args) {
13
+ const [context] = args;
14
+ if (!isValidContext(context)) {
15
+ throw new Error(`The first argument of ${String(propertyKey)} must be an instance of Interaction, Message, or MessageReaction.`);
16
+ }
17
+ // Iterate over each guard and check if it allows the method to proceed
18
+ for (const guard of guards){
19
+ let guardInstance;
20
+ if (isGuardWithParams(guard)) {
21
+ const { provide, params } = guard;
22
+ guardInstance = mainContainer.get(provide, {
23
+ autobind: true
24
+ });
25
+ // Inject the parameters into the guard instance
26
+ Object.assign(guardInstance, params);
27
+ } else {
28
+ // Resolve guard without parameters
29
+ guardInstance = mainContainer.get(guard, {
30
+ autobind: true
31
+ });
32
+ }
33
+ // Ensure the guard has the necessary method `canActivate`
34
+ if (!guardInstance.canActivate) {
35
+ throw new Error(`Guard ${guard.constructor.name} applied to ${String(propertyKey)} does not have a valid canActivate method.`);
36
+ }
37
+ // Check if the guard allows the method to proceed
38
+ const canActivate = await guardInstance.canActivate(...args);
39
+ if (!canActivate) {
40
+ return; // Prevent method execution if the guard fails
41
+ }
42
+ }
43
+ // Call the original method if all guards pass
44
+ return originalMethod.apply(this, args);
45
+ };
46
+ }
47
+ /**
48
+ * `@Guard()` decorator to mark a class as a Guard that later can be added on `@UseGuard` decorator.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * @Guard()
53
+ export class ButtonInteractionGuard implements GuardInterface {
54
+ private readonly logger = new Logger(ButtonInteractionGuard.name)
55
+
56
+ async canActivate(context: ButtonInteraction, { ownerId }: { ownerId: string }): Promise<boolean> {
57
+ if (context.user.id !== ownerId) {
58
+ this.logger.error(
59
+ `User with id ${context.user.id} is not allowed to use this command that initiated by user with id ${ownerId}.`,
60
+ )
61
+ const embed = generateErrorEmbed(
62
+ `Hi <@${context.user.id}>, this command can only be used by the person who initiated it: <@${ownerId}>.`,
63
+ )
64
+ await context.reply({
65
+ embeds: [embed],
66
+ flags: MessageFlagsBitField.Flags.Ephemeral,
67
+ })
68
+ return false
69
+ }
70
+ return true
71
+ }
72
+ }
73
+ * ```
74
+ */ function Guard() {
75
+ return function(target) {
76
+ // Check if the class is already injectable; if not, make it injectable dynamically
77
+ if (!Reflect.hasMetadata('inversify:injectable', target)) {
78
+ injectable()(target);
79
+ }
80
+ mainContainer.bind(target).toSelf().inTransientScope();
81
+ // Bind any dependencies that the guard requires
82
+ const injectables = Reflect.getMetadata('design:paramtypes', target) || [];
83
+ injectables.forEach((dep)=>{
84
+ if (!mainContainer.isBound(dep)) {
85
+ mainContainer.bind(dep).toSelf().inSingletonScope();
86
+ }
87
+ });
88
+ };
89
+ }
90
+ /**
91
+ * Type guard to check if the object is a GuardWithParams.
92
+ * This function helps to check whether a guard is parameterized or not.
93
+ *
94
+ * @param guard - The guard to check.
95
+ * @returns `true` if the guard has parameters, otherwise `false`.
96
+ */ function isGuardWithParams(guard) {
97
+ return typeof guard === 'object' && 'provide' in guard && 'params' in guard;
98
+ }
99
+ /**
100
+ * `@UseGuard()` decorator to apply one or more guards to methods.
101
+ * Guards are used to handle permission checks before executing a method.
102
+ * Each guard must use `@Guard` decorator and implement the `canActivate` method, which determines
103
+ * whether the method should be allowed to execute based on the provided context (Interaction, Message, or Reaction) and arguments.
104
+ * This decorator ensures that all guards pass validation before calling the original method.
105
+ * Supports guards that are parameterized (accepting additional parameters).
106
+ *
107
+ * @param guards - One or more guard classes to apply. These can be regular guards or guards with additional parameters.
108
+ * - If providing a guard with parameters, it should be an object with:
109
+ * - `provide`: The guard class to instantiate. Must implement `GuardInterface`.
110
+ * - `params`: A record of key-value pairs to be passed as additional properties to the guard instance.
111
+ * @returns A method decorator function that applies the guards to the method.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * // Method-level usage
116
+ * @Command('profile-{id}', CommandType.BUTTON)
117
+ * @UseGuard(
118
+ * { provide: RateLimiterGuard, params: { limit: 2, window: 3000 } },
119
+ * ButtonInteractionGuard
120
+ * )
121
+ * async showProfileById(interaction: ButtonInteraction, { id }: { id: string }) {
122
+ * await interaction.reply(`Profile ID: ${id}`)
123
+ * }
124
+ *
125
+ * // Class-level usage
126
+ * @Controller()
127
+ * @UseGuard(GlobalGuard)
128
+ * class MyController {
129
+ * @Command('ping', CommandType.SLASH)
130
+ * async ping(interaction: ChatInputCommandInteraction) {
131
+ * await interaction.reply('Pong!')
132
+ * }
133
+ * }
134
+ * ```
135
+ */ function UseGuard(...guards) {
136
+ return function(target, propertyKey, descriptor) {
137
+ if (descriptor && propertyKey) {
138
+ // Method Decorator
139
+ applyGuards(descriptor, guards, String(propertyKey));
140
+ // Store guard metadata for later access (if needed)
141
+ Reflect.defineMetadata('guards', guards, target, propertyKey);
142
+ } else if (typeof target === 'function' && !propertyKey && !descriptor) {
143
+ // Class Decorator
144
+ const prototype = target.prototype;
145
+ // 1. Get all methods to guard
146
+ const methods = new Set();
147
+ const commandMap = getCommandMap(prototype) || {};
148
+ Object.values(commandMap).flat().forEach((cmd)=>methods.add(cmd.methodName));
149
+ const messageHandlers = getMessageHandlers(prototype) || [];
150
+ messageHandlers.forEach((handler)=>methods.add(handler.method));
151
+ const reactionHandlers = getReactionHandlers(prototype) || [];
152
+ reactionHandlers.forEach((handler)=>methods.add(handler.method));
153
+ for (const methodName of methods){
154
+ const methodDescriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
155
+ if (methodDescriptor) {
156
+ applyGuards(methodDescriptor, guards, methodName);
157
+ Object.defineProperty(prototype, methodName, methodDescriptor);
158
+ Reflect.defineMetadata('guards', guards, prototype, methodName);
159
+ }
160
+ }
161
+ }
162
+ };
163
+ }
164
+
165
+ export { Guard, UseGuard };
@@ -0,0 +1,6 @@
1
+ export { Service } from './service.decorator.js';
2
+ export { CommandBuilder } from './command-builder.decorator.js';
3
+ export { Command, Controller, MessageHandler, ReactionHandler, getCommandMap, getMessageHandlers, getReactionHandlers } from './controller.decorator.js';
4
+ export { Guard, UseGuard } from './guard.decorator.js';
5
+ export { MeoCord } from './app.decorator.js';
6
+ export { mainContainer } from './container.js';
@@ -0,0 +1,58 @@
1
+ import 'reflect-metadata';
2
+ import { mainContainer } from './container.js';
3
+ import { injectable } from 'inversify';
4
+ import { Client } from 'discord.js';
5
+
6
+ /**
7
+ * `@Service()` decorator to mark a class as a service that can be injected into controllers or used as standalone services.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * @Service()
12
+ * class MyService {
13
+ * constructor(private anotherService: AnotherService) {}
14
+ *
15
+ * doSomething() {
16
+ * this.anotherService.alsoDoSomething()
17
+ * console.log('Hello, World!')
18
+ * }
19
+ * }
20
+ * ```
21
+ * @returns A decorator function to apply to the class.
22
+ */ function Service() {
23
+ return function(target) {
24
+ // Check if the class is already injectable; if not, make it injectable dynamically
25
+ if (!Reflect.hasMetadata('inversify:injectable', target)) {
26
+ injectable()(target);
27
+ }
28
+ // Check if the class is already injectable; if not, make it injectable dynamically
29
+ if (!mainContainer.isBound(target)) {
30
+ // Bind the target class to the container in a singleton scope
31
+ mainContainer.bind(target).toSelf().inSingletonScope();
32
+ }
33
+ // Recursively bind dependencies
34
+ bindDependencies(target);
35
+ };
36
+ }
37
+ function bindDependencies(target) {
38
+ // Get the constructor parameter types using Reflect metadata
39
+ const dependencies = Reflect.getMetadata('design:paramtypes', target) || [];
40
+ dependencies.forEach((dep)=>{
41
+ // Bind the dependency if not already bound
42
+ if (!mainContainer.isBound(dep)) {
43
+ if (dep.name === Client.name) return;
44
+ try {
45
+ // Check if the class is already injectable; if not, make it injectable dynamically
46
+ if (!Reflect.hasMetadata('inversify:injectable', dep)) {
47
+ injectable()(dep);
48
+ }
49
+ mainContainer.bind(dep).toSelf().inSingletonScope();
50
+ bindDependencies(dep); // Recur for the dependencies of the current dependency
51
+ } catch (error) {
52
+ console.warn(`Could not bind dependency: ${dep?.name || dep}`, error);
53
+ }
54
+ }
55
+ });
56
+ }
57
+
58
+ export { Service };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * MeoCord Framework
3
+ * Copyright (C) 2025 Ukasyah Rahmatullah Zada
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */ var ControllerType = /*#__PURE__*/ function(ControllerType) {
18
+ ControllerType["BUTTON"] = "button";
19
+ ControllerType["MODAL_SUBMIT"] = "modal-submit";
20
+ ControllerType["SELECT_MENU"] = "select-menu";
21
+ ControllerType["REACTION"] = "reaction";
22
+ ControllerType["MESSAGE"] = "message";
23
+ ControllerType["SLASH"] = "slash";
24
+ ControllerType["CONTEXT_MENU"] = "context-menu";
25
+ return ControllerType;
26
+ }({});
27
+ var CommandType = /*#__PURE__*/ function(CommandType) {
28
+ CommandType["SLASH"] = "SLASH";
29
+ CommandType["BUTTON"] = "BUTTON";
30
+ CommandType["CONTEXT_MENU"] = "CONTEXT_MENU";
31
+ CommandType["SELECT_MENU"] = "SELECT_MENU";
32
+ CommandType["MODAL_SUBMIT"] = "MODAL_SUBMIT";
33
+ return CommandType;
34
+ }({});
35
+ /**
36
+ * Enum representing actions that can be performed on a message reaction.
37
+ */ var ReactionHandlerAction = /*#__PURE__*/ function(ReactionHandlerAction) {
38
+ /** Reaction added to a message. */ ReactionHandlerAction["ADD"] = "ADD";
39
+ /** Reaction removed from a message. */ ReactionHandlerAction["REMOVE"] = "REMOVE";
40
+ return ReactionHandlerAction;
41
+ }({});
42
+
43
+ export { CommandType, ControllerType, ReactionHandlerAction };
@@ -0,0 +1 @@
1
+ export { CommandType, ReactionHandlerAction } from './controller.enum.js';
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,5 @@
1
+ var version = "1.2.2-0";
2
+ var packageJson = {
3
+ version: version};
4
+
5
+ export { packageJson as default, version };
@@ -0,0 +1,68 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { loadMeoCordConfig } from './meocord-config-loader.util.js';
4
+ import wait from './wait.util.js';
5
+ import chalk from 'chalk';
6
+
7
+ /**
8
+ * Finds the package directory for the given module name
9
+ * @param {string} moduleName - The name of the module to find
10
+ * @param {string} baseDir - The starting directory to search from (defaults to process.cwd())
11
+ * @returns {string} - The directory path where the module's package.json is located
12
+ */ const findModulePackageDir = (moduleName, baseDir = process.cwd())=>{
13
+ try {
14
+ // Resolve the node_modules directory from the base directory
15
+ let currentDir = baseDir;
16
+ // Traverse the node_modules directories upwards until the package is found
17
+ while(currentDir !== path.parse(currentDir).root){
18
+ const modulePath = path.join(currentDir, 'node_modules', moduleName);
19
+ if (fs.existsSync(modulePath)) {
20
+ return modulePath // Return the full path to the module directory
21
+ ;
22
+ }
23
+ // Move up one level in the directory structure
24
+ currentDir = path.join(currentDir, '..');
25
+ }
26
+ throw new Error(`Module ${moduleName} not found in node_modules.`);
27
+ } catch (error) {
28
+ if (error instanceof Error) {
29
+ console.error(chalk.red(`Error finding package directory for ${moduleName}:`, error.message));
30
+ } else {
31
+ console.error(chalk.red(`Error finding package directory for ${moduleName}:`, error));
32
+ }
33
+ return null;
34
+ }
35
+ };
36
+ /**
37
+ * Compiles and validates the MeoCord configuration file.
38
+ * Ensures that `meocord.config.ts` exists, compiles it, and checks
39
+ * the presence of essential configuration properties like `discordToken`.
40
+ *
41
+ * @throws Will exit the process if the `meocord.config.ts` file is missing or
42
+ * if the `discordToken` property is not found in the configuration.
43
+ */ async function compileAndValidateConfig() {
44
+ const meocordConfigPath = path.resolve(process.cwd(), 'meocord.config.ts');
45
+ if (!fs.existsSync(meocordConfigPath)) {
46
+ console.error(chalk.red('Configuration file "meocord.config.ts" is missing!'));
47
+ await wait(100);
48
+ process.exit(1);
49
+ }
50
+ const meocordConfig = loadMeoCordConfig();
51
+ if (!meocordConfig?.discordToken) {
52
+ console.error(chalk.red('Discord token is missing!'));
53
+ await wait(100);
54
+ process.exit(1);
55
+ }
56
+ }
57
+ /**
58
+ * Sets the environment mode for the application.
59
+ * Assigns the provided mode to `process.env.NODE_ENV` if it hasn't been set already.
60
+ *
61
+ * @param {'production' | 'development'} mode - The desired environment mode.
62
+ */ function setEnvironment(mode) {
63
+ if (!process.env.NODE_ENV) {
64
+ process.env.NODE_ENV = mode;
65
+ }
66
+ }
67
+
68
+ export { compileAndValidateConfig, findModulePackageDir, setEnvironment };
@@ -0,0 +1,13 @@
1
+ import { EmbedBuilder } from 'discord.js';
2
+ import '../common/logger.js';
3
+ import { Theme } from '../common/theme.js';
4
+
5
+ const createErrorEmbed = (description)=>{
6
+ const embed = new EmbedBuilder();
7
+ embed.setColor(Theme.errorColor);
8
+ embed.setTitle('Oops!');
9
+ embed.setDescription(description);
10
+ return embed;
11
+ };
12
+
13
+ export { createErrorEmbed };
@@ -0,0 +1,107 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { exec } from 'child_process';
4
+ import { Logger } from '../common/logger.js';
5
+ import '../common/theme.js';
6
+ import { kebabCase, startCase, camelCase } from 'lodash-es';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename$1 = fileURLToPath(import.meta.url);
10
+ const __dirname$1 = path.dirname(__filename$1);
11
+ const logger = new Logger('MeoCord');
12
+ /**
13
+ * Converts a given name to a properly formatted class name.
14
+ * @param originalName - The original name to be converted to a class name.
15
+ * @returns The formatted class name.
16
+ * @throws Will exit the process if the generated class name is invalid.
17
+ */ function toClassName(originalName) {
18
+ const className = startCase(camelCase(originalName)).replace(/\s/g, '');
19
+ const classNameRegex = /^[A-Z][A-Za-z0-9]*$/;
20
+ if (!classNameRegex.test(className)) {
21
+ logger.error(`Invalid class name "${originalName}". Must start with a letter and contain alphanumeric characters.`);
22
+ process.exit(1);
23
+ }
24
+ return className;
25
+ }
26
+ /**
27
+ * Validates and formats a given name, splitting it into parts,
28
+ * converting it to kebab-case, and generating a class name.
29
+ * @param originalName - The name to validate and format. It can include slashes for nested paths.
30
+ * @returns An object containing the name parts, kebab-case name, and class name.
31
+ * @throws Will exit the process if the name is undefined, invalid,
32
+ * or the generated class name is invalid.
33
+ */ function validateAndFormatName(originalName) {
34
+ if (!originalName) {
35
+ logger.error('Guard name is required.');
36
+ process.exit(1);
37
+ }
38
+ const parts = originalName.split('/');
39
+ const fileName = parts.pop();
40
+ if (!fileName) {
41
+ logger.error('Invalid guard name.');
42
+ process.exit(1);
43
+ }
44
+ const kebabCaseName = kebabCase(fileName);
45
+ const className = toClassName(fileName);
46
+ return {
47
+ parts,
48
+ kebabCaseName,
49
+ className
50
+ };
51
+ }
52
+ /**
53
+ * Ensures that a given directory exists. Creates the directory and any necessary parent directories if they do not exist.
54
+ * @param directory - The absolute path of the directory to create.
55
+ */ function createDirectoryIfNotExists(directory) {
56
+ if (!fs.existsSync(directory)) {
57
+ fs.mkdirSync(directory, {
58
+ recursive: true
59
+ });
60
+ }
61
+ }
62
+ /**
63
+ * Writes the provided content to a file and runs ESLint on the file for formatting.
64
+ * @param filePath - The absolute path of the file to create or overwrite.
65
+ * @param content - The content to write to the file.
66
+ * @throws Logs an error if the file creation or ESLint command fails.
67
+ */ function generateFile(filePath, content) {
68
+ try {
69
+ fs.writeFileSync(filePath, content);
70
+ logger.log(`Guard file created at: ${path.relative(process.cwd(), filePath)}`);
71
+ exec(`npx eslint --fix ${filePath}`);
72
+ } catch (error) {
73
+ logger.error(`Failed to create guard file at ${filePath}`, error);
74
+ }
75
+ }
76
+ /**
77
+ * Builds and returns a template string for a given class name using a specific template file.
78
+ * @param className - The name of the class to insert into the template.
79
+ * @param templateFileName - The name of the template file to use.
80
+ * @returns The populated template string.
81
+ * @throws Will throw an error if the template file cannot be read.
82
+ */ function buildTemplate(className, templateFileName) {
83
+ const filePath = path.resolve(__dirname$1, '..', 'bin', 'builder-template', templateFileName);
84
+ let template = fs.readFileSync(filePath, 'utf-8');
85
+ const variables = {
86
+ className
87
+ };
88
+ for (const [key, value] of Object.entries(variables)){
89
+ template = template.replaceAll(`{{${key}}}`, value);
90
+ }
91
+ return template;
92
+ }
93
+ /**
94
+ * Populates a template file with the provided variables by replacing placeholders in the template.
95
+ * @param filePath - The path to the template file.
96
+ * @param variables - An object containing variable names and their replacement values.
97
+ * @returns The populated template string.
98
+ * @throws Will throw an error if the template file cannot be read.
99
+ */ function populateTemplate(filePath, variables) {
100
+ let template = fs.readFileSync(filePath, 'utf-8');
101
+ for (const [key, value] of Object.entries(variables)){
102
+ template = template.replaceAll(`{{${key}}}`, value);
103
+ }
104
+ return template;
105
+ }
106
+
107
+ export { buildTemplate, createDirectoryIfNotExists, generateFile, populateTemplate, toClassName, validateAndFormatName };
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * You should have received a copy of the GNU General Public License
16
16
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
- *//**
17
+ */ /**
18
18
  * Helper function to fix common JSON formatting issues in tsconfig.json, such as:
19
19
  * - Removing single-line comments.
20
20
  * - Removing trailing commas.
@@ -22,8 +22,12 @@
22
22
  *
23
23
  * @param {string} jsonString - The raw JSON string to fix.
24
24
  * @returns {string} The corrected JSON string.
25
- */export function fixJSON(a){return a.replace(/\/\/.*$/gm,"")// Remove single-line comments
26
- .replace(/,(\s*[}\]])/g,"$1")// Remove trailing commas before } or ]
27
- .replace(/,\s*$/,"")// Remove trailing commas at the end of the file
28
- .replace(/^\s*[\r\n]/gm,"");// Replace empty lines only
29
- }
25
+ */ function fixJSON(jsonString) {
26
+ return jsonString.replace(/\/\/.*$/gm, '') // Remove single-line comments
27
+ .replace(/,(\s*[}\]])/g, '$1') // Remove trailing commas before } or ]
28
+ .replace(/,\s*$/, '') // Remove trailing commas at the end of the file
29
+ .replace(/^\s*[\r\n]/gm, '') // Replace empty lines only
30
+ ;
31
+ }
32
+
33
+ export { fixJSON };
@@ -0,0 +1,172 @@
1
+ import CliTable3 from 'cli-table3';
2
+ import { findModulePackageDir } from './common.util.js';
3
+ import path from 'node:path';
4
+ import fs__default from 'node:fs';
5
+ import wait from './wait.util.js';
6
+ import chalk from 'chalk';
7
+
8
+ function configureCommandHelp(command) {
9
+ command.configureHelp({
10
+ formatHelp: (cmd, helper)=>{
11
+ return formatHelp(cmd, helper, command.options);
12
+ }
13
+ });
14
+ command.commands.forEach((cmd)=>{
15
+ configureCommandHelp(cmd);
16
+ });
17
+ }
18
+ /**
19
+ * Formats the help output for a command.
20
+ *
21
+ * @param cmd - The command for which to format help.
22
+ * @param helper - The helper object for formatting.
23
+ * @param options - The options available for the command.
24
+ * @returns The formatted help text.
25
+ */ function formatHelp(cmd, helper, options) {
26
+ let helpText = `MeoCord Copyright (C) 2025 Ukasyah Rahmatullah Zada
27
+ This program comes with ABSOLUTELY NO WARRANTY; for details type \`meocord show -w'.
28
+ This is free software, and you are welcome to redistribute it
29
+ under certain conditions; type \`meocord show -c' for details.\n\n`;
30
+ helpText += `${helper.commandUsage(cmd)}\n\n`;
31
+ helpText += `${helper.commandDescription(cmd)}\n\n`;
32
+ if (cmd.registeredArguments.length > 0) {
33
+ helpText += generateArgumentsTable(cmd.registeredArguments);
34
+ helpText += '\n\n';
35
+ }
36
+ if (options.length > 0) {
37
+ helpText += 'Available Options:\n';
38
+ helpText += generateOptionsTable(options, helper);
39
+ helpText += '\n\n';
40
+ }
41
+ if (cmd.commands.length > 0) {
42
+ helpText += 'Available Commands:\n';
43
+ helpText += generateCommandsTable(cmd);
44
+ helpText += '\n';
45
+ }
46
+ return helpText;
47
+ }
48
+ /**
49
+ * Generates a table of commands with their aliases and descriptions.
50
+ *
51
+ * @param cmd - The command for which to generate the table.
52
+ * @returns The formatted table of commands.
53
+ */ function generateCommandsTable(cmd) {
54
+ const table = new CliTable3({
55
+ head: [
56
+ 'Command',
57
+ 'Alias',
58
+ 'Description'
59
+ ]
60
+ });
61
+ cmd.commands.forEach((cmd)=>{
62
+ const alias = cmd.aliases().length > 0 ? cmd.aliases().join(', ') : '—';
63
+ const description = cmd.description() || 'No description provided';
64
+ table.push([
65
+ cmd.name(),
66
+ alias,
67
+ description
68
+ ]);
69
+ });
70
+ return table.toString();
71
+ }
72
+ /**
73
+ * Generates a table of commands with their aliases and descriptions.
74
+ *
75
+ * @param options
76
+ * @param helper
77
+ * @returns The formatted table of commands.
78
+ */ function generateOptionsTable(options, helper) {
79
+ const table = new CliTable3({
80
+ head: [
81
+ 'Option',
82
+ 'Description'
83
+ ]
84
+ });
85
+ options.forEach((option)=>table.push([
86
+ helper.optionTerm(option),
87
+ helper.optionDescription(option)
88
+ ]));
89
+ return table.toString();
90
+ }
91
+ /**
92
+ * Generates a formatted table of arguments for the specified command.
93
+ *
94
+ * This method creates two tables:
95
+ * 1. A table displaying argument names and their descriptions.
96
+ * 2. (If applicable) A table showing available choices for arguments with predefined choices.
97
+ *
98
+ * @param args - The list of arguments for the command.
99
+ * @returns A string representation of the formatted tables, including available arguments and their choices.
100
+ */ function generateArgumentsTable(args) {
101
+ const table = new CliTable3({
102
+ head: [
103
+ 'Argument',
104
+ 'Description'
105
+ ]
106
+ });
107
+ const choiceTable = new CliTable3({
108
+ head: [
109
+ 'Choice'
110
+ ]
111
+ });
112
+ let hasChoices = false;
113
+ args.forEach((arg)=>{
114
+ if (arg.argChoices) {
115
+ hasChoices = true;
116
+ arg.argChoices.forEach((choice)=>{
117
+ choiceTable.push([
118
+ choice
119
+ ]);
120
+ });
121
+ }
122
+ table.push([
123
+ arg.name(),
124
+ arg.description || 'No description provided'
125
+ ]);
126
+ });
127
+ let text = '';
128
+ if (hasChoices) {
129
+ text += 'Available Choices:\n';
130
+ text += choiceTable.toString();
131
+ text += '\n\n';
132
+ } else {
133
+ text += 'No available choices.\n\n';
134
+ }
135
+ text += 'Available Arguments:\n';
136
+ text += table.toString();
137
+ return text;
138
+ }
139
+ /**
140
+ * Ensures that the script is being run from the root directory of the project.
141
+ * Validates the existence of required files, dependencies, and configuration.
142
+ * If validation fails, it logs an error message and terminates the process.
143
+ */ async function ensureReady() {
144
+ const meocordPath = findModulePackageDir('meocord');
145
+ const packageJsonPath = path.resolve(process.cwd(), 'package.json');
146
+ try {
147
+ // Ensure the root package.json exists
148
+ if (!fs__default.existsSync(packageJsonPath)) {
149
+ throw new Error('package.json not found. This script must be run from the root directory of the project.');
150
+ }
151
+ // Ensure the MeoCord package directory is found
152
+ if (!meocordPath) {
153
+ throw new Error('Cannot locate the "MeoCord" package directory.');
154
+ }
155
+ // Read and parse the root package.json
156
+ const { dependencies } = JSON.parse(fs__default.readFileSync(packageJsonPath, 'utf-8'));
157
+ // Read and parse the MeoCord package.json
158
+ const internalPackageJsonPath = path.join(meocordPath, 'package.json');
159
+ const { name: internalPackageName } = JSON.parse(fs__default.readFileSync(internalPackageJsonPath, 'utf-8'));
160
+ // Validate that MeoCord is listed as a dependency in the root package.json
161
+ if (!dependencies?.[internalPackageName]) {
162
+ throw new Error('The package.json does not list "MeoCord" as a dependency. Ensure you are in the root directory.');
163
+ }
164
+ } catch (error) {
165
+ // Log the error and exit the process
166
+ console.error(chalk.red(error instanceof Error ? error.message : 'An unknown error occurred during validation.'));
167
+ await wait(100);
168
+ process.exit(1);
169
+ }
170
+ }
171
+
172
+ export { configureCommandHelp, ensureReady, formatHelp, generateArgumentsTable, generateCommandsTable, generateOptionsTable };