UNPKG

meocord

Version:

Decorator-based Discord bot framework built on discord.js. Brings NestJS-style controllers, dependency injection, guards, and testing utilities to bot development — with a full CLI and TypeScript-first design.

180 lines (173 loc) 6.75 kB
'use strict'; var node_util = require('node:util'); var dayjs = require('dayjs'); var utc = require('dayjs/plugin/utc.js'); var timezone = require('dayjs/plugin/timezone.js'); var path = require('path'); var fs = require('fs'); var jiti = require('jiti'); var chalk = require('chalk'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; /** * MeoCord Framework * Copyright (c) 2025 Ukasyah Rahmatullah Zada * SPDX-License-Identifier: MIT */ /** * Helper function to fix common JSON formatting issues in tsconfig.json, such as: * - Removing single-line comments. * - Removing trailing commas. * - Stripping newlines. * * @param {string} jsonString - The raw JSON string to fix. * @returns {string} The corrected JSON string. */ function fixJSON(jsonString) { return jsonString.replace(/\/\/.*$/gm, '') // Remove single-line comments .replace(/,(\s*[}\]])/g, '$1') // Remove trailing commas before } or ] .replace(/,\s*$/, '') // Remove trailing commas at the end of the file .replace(/^\s*[\r\n]/gm, '') // Replace empty lines only ; } let cachedConfig; let configLoaded = false; /** * Loads the MeoCord configuration, checking compiled output first then falling back to source. * * Resolution order: * 1. `dist/meocord.config.mjs` — pre-compiled by `meocord build`, no tsconfig or source files needed * 2. `meocord.config.ts` — loaded via jiti with tsconfig path aliases (dev mode) * * The result is cached after the first successful load. * * @returns {MeoCordConfig | undefined} The loaded configuration object, or undefined if loading fails. */ function loadMeoCordConfig() { if (configLoaded) return cachedConfig; configLoaded = true; cachedConfig = loadCompiledConfig() ?? loadSourceConfig(); return cachedConfig; } /** * Loads the pre-compiled config from dist/meocord.config.mjs. * This file is generated by `meocord build` and requires no tsconfig or source files. */ function loadCompiledConfig() { const compiledPath = path.resolve(process.cwd(), 'dist', 'meocord.config.mjs'); if (!fs.existsSync(compiledPath)) return undefined; try { const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-Bz-D4RbT.cjs', document.baseURI).href)), { interopDefault: true }); return jiti$1(compiledPath); } catch { // Fall through to source config return undefined; } } /** * Loads the source config from meocord.config.ts via jiti with tsconfig path alias resolution. * Used in development mode where source files and tsconfig.json are available. */ function loadSourceConfig() { const configPath = path.resolve(process.cwd(), 'meocord.config.ts'); if (!fs.existsSync(configPath)) return undefined; try { const tsConfigPath = path.resolve(process.cwd(), 'tsconfig.json'); const aliases = {}; if (fs.existsSync(tsConfigPath)) { const tsConfig = JSON.parse(fixJSON(fs.readFileSync(tsConfigPath, 'utf-8'))); const paths = tsConfig?.compilerOptions?.paths; if (paths) { for (const [key, values] of Object.entries(paths)){ const aliasKey = key.replace('/*', ''); aliases[aliasKey] = path.resolve(process.cwd(), values[0].replace('/*', '')); } } } const jiti$1 = jiti.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('_shared/theme-Bz-D4RbT.cjs', document.baseURI).href)), { interopDefault: true, alias: aliases, moduleCache: false }); return jiti$1(configPath); } catch (error) { if (error instanceof Error) { console.error(`[MeoCord] Failed to load config: ${error.message}`); } else { console.error(`[MeoCord] Failed to load config: Unknown error`); } return undefined; } } dayjs.extend(utc); dayjs.extend(timezone); class Logger { log(...args) { this.logWithContext('log', args); } info(...args) { this.logWithContext('log', args); } warn(...args) { this.logWithContext('warn', args); } error(...args) { this.logWithContext('error', args); } debug(...args) { this.logWithContext('debug', args); } verbose(...args) { this.logWithContext('log', args); } formatMessage(message, logType) { if (typeof message === 'object' && message !== null) { return node_util.inspect(message, { showHidden: true, depth: null, colors: true, compact: false, showProxy: true }); } return (this.colorMap[logType] || ((msg)=>msg))(message); } logWithContext(logLevel, messages) { if (messages.length === 0) return; const config = loadMeoCordConfig(); const logType = logLevel.toUpperCase(); const applyColor = this.colorMap[logType] || ((msg)=>msg); const formattedMessages = messages.map((message)=>this.formatMessage(message, logType)); const coloredAppName = config?.appName ? applyColor(chalk.bold(`[${config.appName}]`)) : undefined; const timestamp = chalk.bold(dayjs().format('dddd, MMMM D, YYYY HH:mm:ss [UTC]Z')); const coloredLogLevel = applyColor(chalk.bold(`[${logType}]`)); const coloredContext = this.context ? chalk.yellow.bold(`[${this.context}]`) : ''; const logTexts = [ coloredAppName, timestamp, coloredLogLevel, coloredContext, ...formattedMessages ].filter((log)=>!!log); console[logLevel](...logTexts); } constructor(context){ this.context = context; this.colorMap = { LOG: chalk.green, INFO: chalk.cyan, WARN: chalk.yellow, ERROR: chalk.red, DEBUG: chalk.magenta }; } } /** * MeoCord Framework * Copyright (c) 2025 Ukasyah Rahmatullah Zada * SPDX-License-Identifier: MIT */ class Theme { } Theme.successColor = '#28A745'; Theme.infoColor = '#17A2B8'; Theme.errorColor = '#DC3545'; Theme.warningColor = '#FFC107'; exports.Logger = Logger; exports.Theme = Theme; exports.loadMeoCordConfig = loadMeoCordConfig;