UNPKG

create-discord-forge

Version:

A CLI made with Commander to create a Discord and easily add advanced features to your bot made with discord.js in TypeScript or JavaScript

273 lines (272 loc) 13.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.handleAddCommand = handleAddCommand; const inquirer_1 = __importDefault(require("inquirer")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const ts_morph_1 = require("ts-morph"); const ora_1 = __importDefault(require("ora")); const AVAILABLE_FEATURES = ['ban', 'kick', 'warn']; async function handleAddCommand(typescript, features) { let selectedFeatures = []; let ts = false; if (typeof features === 'object' && features.length > 0) { if (features[features.length - 1].includes('-')) { features.pop(); } } if (!features || (Array.isArray(features) && features.length === 0)) { const { chosen, TYPESCRIPT } = await inquirer_1.default.prompt([ { name: 'TYPESCRIPT', type: 'confirm', message: 'Do you want to use TypeScript?', when: () => !typescript, // Only ask if typescript is not already set default: true, }, { name: 'chosen', type: 'checkbox', message: 'Which features do you want to add?', choices: AVAILABLE_FEATURES, }, ]); selectedFeatures = chosen; ts = !TYPESCRIPT ? typescript : TYPESCRIPT; // Use the TypeScript option from the prompt } else { // If it's a string, put it in an array, otherwise use the array as is selectedFeatures = Array.isArray(features) ? features : [features]; ts = typescript; // Use the provided TypeScript option or default to false } console.log('\n'); const mainLoader = (0, ora_1.default)('Validating selected features...').start(); await new Promise(res => setTimeout(res, 500)); for (const feat of selectedFeatures) { if (!AVAILABLE_FEATURES.includes(feat)) { mainLoader.fail(`Unknown feature: ${feat}`); continue; // Continue with the other features instead of returning } } mainLoader.succeed(`${selectedFeatures.length} feature(s) validated successfully`); for (const feat of selectedFeatures) { if (AVAILABLE_FEATURES.includes(feat)) { await installFeature(feat, ts); } } } async function installFeature(name, ts) { let featureLoader = (0, ora_1.default)(`Installing feature "${name}"...`).start(); const envData = path_1.default.join(process.cwd(), 'components.json'); let parseEnvData; featureLoader.text = 'Checking components.json configuration...'; await new Promise(res => setTimeout(res, 300)); if (!fs_1.default.existsSync(envData)) { featureLoader.warn('components.json not found, using default configuration'); // Default configuration parseEnvData = { ts: ts, aliases: { index: `src/index.${ts ? 'ts' : 'js'}`, components: 'src/components', commands: 'src/commands', }, }; featureLoader = (0, ora_1.default)(`Detecting project type...`).start(); await new Promise(res => setTimeout(res, 500)); featureLoader.succeed(`Project type detected: ${ts ? 'TypeScript' : 'JavaScript'}`); } else { parseEnvData = JSON.parse(fs_1.default.readFileSync(envData, 'utf-8')); featureLoader.succeed('Configuration loaded from components.json'); } const templatePath = path_1.default.join(__dirname, '../../modules/components', name); const metadataPath = path_1.default.join(templatePath, 'meta.json'); featureLoader = (0, ora_1.default)('Validating feature template...').start(); await new Promise(res => setTimeout(res, 300)); if (!fs_1.default.existsSync(templatePath)) { featureLoader.fail(`Feature "${name}" is not implemented yet.`); return; } if (!fs_1.default.existsSync(metadataPath)) { featureLoader.fail(`meta.json not found for feature "${name}"`); return; } const metadata = JSON.parse(fs_1.default.readFileSync(metadataPath, 'utf-8')); const subFolder = metadata.ranking; const targetBasePath = process.cwd(); featureLoader.succeed('Feature template validated successfully'); // First, automatically copy all event files to components featureLoader = (0, ora_1.default)('Copying event files...').start(); await new Promise(res => setTimeout(res, 500)); const eventsPath = path_1.default.join(templatePath, 'files', 'events'); if (fs_1.default.existsSync(eventsPath)) { const eventFiles = fs_1.default .readdirSync(eventsPath) .filter(file => file.endsWith(parseEnvData.ts ? '.ts' : '.js')); for (const eventFile of eventFiles) { const sourcePath = path_1.default.join(eventsPath, eventFile); // Use the components alias to determine the destination path const targetSourcePath = parseEnvData.aliases?.components || 'src/components'; const targetPath = path_1.default.join(targetBasePath, targetSourcePath, subFolder, eventFile); copyFileWithAliases(sourcePath, targetPath); } featureLoader.succeed(`${eventFiles.length} event file(s) copied successfully`); } else { featureLoader.info('No event files found to copy'); } featureLoader = (0, ora_1.default)('Copying command files...').start(); await new Promise(res => setTimeout(res, 500)); const commandsPath = path_1.default.join(templatePath, 'files', 'commands'); if (fs_1.default.existsSync(commandsPath)) { const commandFiles = fs_1.default .readdirSync(commandsPath) .filter(file => file.endsWith(parseEnvData.ts ? '.ts' : '.js')); for (const commandFile of commandFiles) { const sourcePath = path_1.default.join(commandsPath, commandFile); // Use the commands alias to determine the destination path const targetSourcePath = parseEnvData.aliases?.commands || 'src/commands'; const targetPath = path_1.default.join(targetBasePath, targetSourcePath, subFolder, commandFile); copyFileWithAliases(sourcePath, targetPath); } featureLoader.succeed(`${commandFiles.length} command file(s) copied successfully`); } else { featureLoader.info('No command files found to copy'); } // Count the total number of files copied const eventsCount = fs_1.default.existsSync(path_1.default.join(templatePath, 'files', 'events')) ? fs_1.default .readdirSync(path_1.default.join(templatePath, 'files', 'events')) .filter(file => file.endsWith(parseEnvData.ts ? '.ts' : '.js')).length : 0; const commandsCount = fs_1.default.existsSync(path_1.default.join(templatePath, 'files', 'commands')) ? fs_1.default .readdirSync(path_1.default.join(templatePath, 'files', 'commands')) .filter(file => file.endsWith(parseEnvData.ts ? '.ts' : '.js')).length : 0; const totalFiles = eventsCount + commandsCount; featureLoader = (0, ora_1.default)('Finalizing feature installation...').start(); await new Promise(res => setTimeout(res, 300)); featureLoader.succeed(`Feature "${name}" installed successfully (${parseEnvData.ts ? 'TypeScript' : 'JavaScript'}) - ${totalFiles} files copied`); // Add handlers to the project index await addHandlersToIndex(name, parseEnvData, templatePath, subFolder); } async function addHandlersToIndex(featureName, parseEnvData, templatePath, subFolder) { let indexLoader = (0, ora_1.default)('Updating index file...').start(); await new Promise(res => setTimeout(res, 500)); const project = new ts_morph_1.Project(); const indexAlias = parseEnvData.aliases?.index || `src/index.${parseEnvData.ts ? 'ts' : 'js'}`; const indexPath = path_1.default.join(process.cwd(), indexAlias); if (!fs_1.default.existsSync(indexPath)) { indexLoader.fail(`Index file not found: ${indexPath}`); return; } else { indexLoader.succeed(`Index file found: ${path_1.default.relative(process.cwd(), indexPath)}`); } // Charger le fichier index const sourceFile = project.addSourceFileAtPath(indexPath); // Trouver les fichiers events à importer const eventsPath = path_1.default.join(templatePath, 'files', 'events'); if (!fs_1.default.existsSync(eventsPath)) { indexLoader.info('No event files to import'); return; } else { indexLoader.succeed(`Event files found: ${eventsPath}`); } const eventFiles = fs_1.default .readdirSync(eventsPath) .filter(file => file.endsWith(parseEnvData.ts ? '.ts' : '.js')) .map(file => path_1.default.basename(file, path_1.default.extname(file))); indexLoader = (0, ora_1.default)('Adding imports to index file...').start(); await new Promise(res => setTimeout(res, 300)); // Ajouter les imports const componentsPath = parseEnvData.aliases?.components || 'src/components'; const fullComponentsPath = path_1.default.join(process.cwd(), componentsPath, subFolder); let relativeComponentsPath = path_1.default .relative(path_1.default.dirname(indexPath), fullComponentsPath) .replace(/\\/g, '/'); // S'assurer que le chemin relatif commence par ./ ou ../ if (!relativeComponentsPath.startsWith('.')) { relativeComponentsPath = './' + relativeComponentsPath; } for (const eventFile of eventFiles) { // Vérifier si l'import existe déjà const existingImport = sourceFile .getImportDeclarations() .find(imp => imp.getModuleSpecifierValue().includes(eventFile)); if (!existingImport) { if (parseEnvData.ts) { // TypeScript: import destructuré const namedImports = eventFile.includes('Handler') ? [eventFile, `handle${eventFile.replace('CommandHandler', '')}Modal`] : [eventFile]; sourceFile.addImportDeclaration({ moduleSpecifier: `${relativeComponentsPath}/${eventFile}`, namedImports: namedImports, }); } else { // JavaScript: require destructuré const namedImports = eventFile.includes('Handler') ? [eventFile, `handle${eventFile.replace('CommandHandler', '')}Modal`] : [eventFile]; sourceFile.addStatements(`const { ${namedImports.join(', ')} } = require('${relativeComponentsPath}/${eventFile}');`); } } } indexLoader.succeed(`Imports added to index file: ${path_1.default.relative(process.cwd(), indexPath)}`); indexLoader = (0, ora_1.default)('Adding handler calls to index file...').start(); await new Promise(res => setTimeout(res, 300)); // Trouver où ajouter les appels de handlers (avant client.login) const clientLogin = sourceFile .getDescendantsOfKind(ts_morph_1.SyntaxKind.CallExpression) .find(call => { const expression = call.getExpression(); return (expression.getKind() === ts_morph_1.SyntaxKind.PropertyAccessExpression && expression.getText().includes('client.login')); }); if (clientLogin) { const statement = clientLogin.getFirstAncestorByKind(ts_morph_1.SyntaxKind.ExpressionStatement); if (statement) { // Ajouter les appels de handlers avant client.login() const statementsToAdd = []; for (const eventFile of eventFiles) { const handlerCall = `${eventFile}(client);`; const modalHandlerCall = eventFile.includes('Handler') ? `handle${eventFile.replace('CommandHandler', '')}Modal(client);` : null; // Vérifier si l'appel existe déjà const existingCall = sourceFile.getFullText().includes(handlerCall); if (!existingCall) { statementsToAdd.push(handlerCall); if (modalHandlerCall) { statementsToAdd.push(modalHandlerCall); } } } // Insérer les statements avant client.login() if (statementsToAdd.length > 0) { const statementIndex = sourceFile.getStatements().indexOf(statement); sourceFile.insertStatements(statementIndex, statementsToAdd); } } } // Sauvegarder les modifications await sourceFile.save(); indexLoader.succeed(`Index file updated: ${path_1.default.relative(process.cwd(), indexPath)}`); return; } function copyFileWithAliases(sourcePath, targetPath) { const targetDir = path_1.default.dirname(targetPath); if (!fs_1.default.existsSync(targetDir)) { fs_1.default.mkdirSync(targetDir, { recursive: true }); } fs_1.default.copyFileSync(sourcePath, targetPath); }