UNPKG

@doedja/kbbi-js

Version:

JavaScript library for accessing KBBI (Kamus Besar Bahasa Indonesia) with anti-Cloudflare protection

254 lines (215 loc) 8.5 kB
const path = require('path'); const fs = require('fs'); const chalk = require('chalk'); class Utils { static wrapText(text, width = 80) { if (!text) return ''; if (text.length <= width) return text; const words = text.split(' '); const lines = []; let currentLine = ''; for (const word of words) { if (currentLine.length + word.length + 1 > width) { lines.push(currentLine); currentLine = word; } else { currentLine = currentLine ? currentLine + ' ' + word : word; } } if (currentLine) { lines.push(currentLine); } return lines.join('\n'); } static ensureDirectory(dirPath) { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } return dirPath; } /** * Ensures the debug directory exists and cleans up old files * @param {string} baseDir - Base directory (usually __dirname) * @param {Object} options - Options for debug directory management * @param {number} options.maxFiles - Maximum number of files to keep (default: 10) * @param {number} options.maxAgeDays - Maximum age of files in days (default: 7) * @returns {string} - Path to the debug directory */ static ensureDebugDirectory(baseDir, options = {}) { const { maxFiles = 10, maxAgeDays = 7 } = options; const debugDir = path.join(baseDir, 'debug'); // Create directory if it doesn't exist this.ensureDirectory(debugDir); // Clean up old files this.cleanupDebugFiles(debugDir, { maxFiles, maxAgeDays }); return debugDir; } /** * Cleans up old debug files * @param {string} debugDir - Path to the debug directory * @param {Object} options - Options for cleanup * @param {number} options.maxFiles - Maximum number of files to keep * @param {number} options.maxAgeDays - Maximum age of files in days */ static cleanupDebugFiles(debugDir, options = {}) { const { maxFiles = 10, maxAgeDays = 7 } = options; try { // Get all files in the debug directory const files = fs.readdirSync(debugDir) .filter(file => fs.statSync(path.join(debugDir, file)).isFile()) .map(file => { const filePath = path.join(debugDir, file); const stats = fs.statSync(filePath); return { name: file, path: filePath, mtime: stats.mtime }; }) .sort((a, b) => b.mtime - a.mtime); // Sort by modified time (newest first) // Delete files that exceed the maximum age const now = new Date(); const maxAgeMs = maxAgeDays * 24 * 60 * 60 * 1000; const filesToDelete = []; // First identify files that are too old for (const file of files) { const ageMs = now - file.mtime; if (ageMs > maxAgeMs) { filesToDelete.push(file); } } // Then identify excess files beyond the maximum count const remainingFiles = files.filter(file => !filesToDelete.includes(file)); if (remainingFiles.length > maxFiles) { const excessFiles = remainingFiles.slice(maxFiles); filesToDelete.push(...excessFiles); } // Delete the identified files for (const file of filesToDelete) { fs.unlinkSync(file.path); } if (filesToDelete.length > 0) { console.log(chalk.gray(`Cleaned up ${filesToDelete.length} old debug files`)); } } catch (error) { console.error(chalk.red(`Error cleaning up debug files: ${error.message}`)); } } /** * Saves HTML content to a debug file * @param {string} baseDir - Base directory (usually __dirname) * @param {string} filename - Name of the file to save * @param {string} content - HTML content to save * @returns {boolean} - Whether the file was saved successfully */ static saveDebugFile(baseDir, filename, content) { try { const debugDir = this.ensureDebugDirectory(baseDir); const filePath = path.join(debugDir, filename); fs.writeFileSync(filePath, content); console.log(chalk.gray(`Debug: Saved ${filename}`)); return true; } catch (error) { console.error(chalk.red(`Error saving debug file: ${error.message}`)); return false; } } static buildUrl(word) { return `https://kbbi.kemdikbud.go.id/entri/${encodeURIComponent(word)}`; } static formatOutput(entry, showExamples = true) { if (!entry) return 'No entry data available'; const result = []; // Format entry name with homonym number if present let header = entry.nama || ''; if (entry.nomor) { header += this.toSuperscript(entry.nomor); } result.push(chalk.bold(header)); // Add entry type if available (peribahasa, idiom, etc.) if (entry.jenis) { result.push(chalk.hex('#8B4513')(` ⟨${entry.jenis}⟩`)); // Saddlebrown color with special formatting } // Add root word if available if (entry.rootWord) { result.push(chalk.gray(` Kata Dasar: `) + chalk.white(entry.rootWord)); } // Add etymology if available if (entry.etimologi && entry.etimologi.text) { result.push(chalk.gray(` Etimologi: `) + chalk.yellow(entry.etimologi.text)); } // Format meanings if (entry.makna && entry.makna.length > 0) { result.push(''); // Add empty line before meanings entry.makna.forEach((meaning) => { const meaningNumber = meaning.nomor || ''; result.push(chalk.bold(`Makna #${meaningNumber}`)); // Add word classes if available if (meaning.kelasKata && meaning.kelasKata.length > 0) { const classes = meaning.kelasKata.map(wc => `${wc.kode}${wc.nama ? ` (${wc.nama})` : ''}`).join(','); result.push(chalk.gray(` Kelas Kata: `) + chalk.white(classes)); } // Add definition if (meaning.definisi) { result.push(chalk.gray(` Definisi: `) + chalk.white(meaning.definisi)); } // Add examples if available and requested if (showExamples && meaning.contoh && meaning.contoh.length > 0) { result.push(''); meaning.contoh.forEach((example) => { const exampleNumber = example.nomor || ''; result.push(chalk.bold.italic(` Contoh #${meaningNumber}-${exampleNumber}`)); result.push(chalk.gray(' ') + chalk.italic.cyan(example.teks)); result.push(''); }); } else { result.push(''); } }); } // Format related words if available if (entry.terkait) { // Add derivatives (kata turunan) if available if (entry.terkait.kataTurunan && entry.terkait.kataTurunan.length > 0) { result.push(chalk.bold.magenta('Kata Turunan:')); result.push(this.wrapText(entry.terkait.kataTurunan.map(w => chalk.cyan(w)).join('; '))); } // Add compound words (gabungan kata) if available if (entry.terkait.gabunganKata && entry.terkait.gabunganKata.length > 0) { result.push(''); result.push(chalk.bold.magenta('Gabungan Kata:')); result.push(this.wrapText(entry.terkait.gabunganKata.map(w => chalk.cyan(w)).join('; '))); } // Add proverbs (peribahasa) if available if (entry.terkait.peribahasa && entry.terkait.peribahasa.length > 0) { result.push(''); result.push(chalk.bold.magenta(`Peribahasa (mengandung [${entry.nama}]):`)); result.push(this.wrapText(entry.terkait.peribahasa.map(p => chalk.yellow(p)).join('; '))); } // Add idioms if available if (entry.terkait.idiom && entry.terkait.idiom.length > 0) { result.push(''); result.push(chalk.bold.magenta(`Idiom (mengandung [${entry.nama}]):`)); result.push(this.wrapText(entry.terkait.idiom.map(i => chalk.yellow(i)).join('; '))); } } return result.join('\n'); } static toSuperscript(number) { if (!number) return ''; const superscripts = { '0': '⁰', '1': '¹', '2': '²', '3': '³', '4': '⁴', '5': '⁵', '6': '⁶', '7': '⁷', '8': '⁸', '9': '⁹' }; return number.toString().split('').map(char => superscripts[char] || char).join(''); } static formatJson(data) { try { return JSON.stringify(data, null, 2); } catch (error) { return `Error formatting JSON: ${error.message}`; } } } module.exports = Utils;