Skip to main content
Glama
MIT License
27,120
19,746
  • Linux
  • Apple
configLoad.ts•6.43 kB
import * as fs from 'node:fs/promises'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; import { createJiti } from 'jiti'; import JSON5 from 'json5'; import pc from 'picocolors'; import { RepomixError, rethrowValidationErrorIfZodError } from '../shared/errorHandle.js'; import { logger } from '../shared/logger.js'; import { defaultConfig, defaultFilePathMap, type RepomixConfigCli, type RepomixConfigFile, type RepomixConfigMerged, repomixConfigFileSchema, repomixConfigMergedSchema, } from './configSchema.js'; import { getGlobalDirectory } from './globalDirectory.js'; const defaultConfigPaths = [ 'repomix.config.ts', 'repomix.config.mts', 'repomix.config.cts', 'repomix.config.js', 'repomix.config.mjs', 'repomix.config.cjs', 'repomix.config.json5', 'repomix.config.jsonc', 'repomix.config.json', ]; const getGlobalConfigPaths = () => { const globalDir = getGlobalDirectory(); return defaultConfigPaths.map((configPath) => path.join(globalDir, configPath)); }; const checkFileExists = async (filePath: string): Promise<boolean> => { try { const stats = await fs.stat(filePath); return stats.isFile(); } catch { return false; } }; const findConfigFile = async (configPaths: string[], logPrefix: string): Promise<string | null> => { for (const configPath of configPaths) { logger.trace(`Checking for ${logPrefix} config at:`, configPath); const fileExists = await checkFileExists(configPath); if (fileExists) { logger.trace(`Found ${logPrefix} config at:`, configPath); return configPath; } } return null; }; export const loadFileConfig = async (rootDir: string, argConfigPath: string | null): Promise<RepomixConfigFile> => { if (argConfigPath) { // If a specific config path is provided, use it directly const fullPath = path.resolve(rootDir, argConfigPath); logger.trace('Loading local config from:', fullPath); const isLocalFileExists = await checkFileExists(fullPath); if (isLocalFileExists) { return await loadAndValidateConfig(fullPath); } throw new RepomixError(`Config file not found at ${argConfigPath}`); } // Try to find a local config file using the priority order const localConfigPaths = defaultConfigPaths.map((configPath) => path.resolve(rootDir, configPath)); const localConfigPath = await findConfigFile(localConfigPaths, 'local'); if (localConfigPath) { return await loadAndValidateConfig(localConfigPath); } // Try to find a global config file using the priority order const globalConfigPaths = getGlobalConfigPaths(); const globalConfigPath = await findConfigFile(globalConfigPaths, 'global'); if (globalConfigPath) { return await loadAndValidateConfig(globalConfigPath); } logger.log( pc.dim( `No custom config found at ${defaultConfigPaths.join(', ')} or global config at ${globalConfigPaths.join(', ')}.\nYou can add a config file for additional settings. Please check https://github.com/yamadashy/repomix for more information.`, ), ); return {}; }; const getFileExtension = (filePath: string): string => { const match = filePath.match(/\.(ts|mts|cts|js|mjs|cjs|json5|jsonc|json)$/); return match ? match[1] : ''; }; const loadAndValidateConfig = async (filePath: string): Promise<RepomixConfigFile> => { try { let config: unknown; const ext = getFileExtension(filePath); switch (ext) { case 'ts': case 'mts': case 'cts': case 'js': case 'mjs': case 'cjs': { // Use jiti for TypeScript and JavaScript files // This provides consistent behavior and avoids Node.js module cache issues const jiti = createJiti(import.meta.url, { moduleCache: false, // Disable cache to ensure fresh config loads interopDefault: true, // Automatically use default export }); config = await jiti.import(pathToFileURL(filePath).href); break; } case 'json5': case 'jsonc': case 'json': { // Use JSON5 for JSON/JSON5/JSONC files const fileContent = await fs.readFile(filePath, 'utf-8'); config = JSON5.parse(fileContent); break; } default: throw new RepomixError(`Unsupported config file format: ${filePath}`); } return repomixConfigFileSchema.parse(config); } catch (error) { rethrowValidationErrorIfZodError(error, 'Invalid config schema'); if (error instanceof SyntaxError) { throw new RepomixError(`Invalid syntax in config file ${filePath}: ${error.message}`); } if (error instanceof Error) { throw new RepomixError(`Error loading config from ${filePath}: ${error.message}`); } throw new RepomixError(`Error loading config from ${filePath}`); } }; export const mergeConfigs = ( cwd: string, fileConfig: RepomixConfigFile, cliConfig: RepomixConfigCli, ): RepomixConfigMerged => { logger.trace('Default config:', defaultConfig); const baseConfig = defaultConfig; // If the output file path is not provided in the config file or CLI, use the default file path for the style if (cliConfig.output?.filePath == null && fileConfig.output?.filePath == null) { const style = cliConfig.output?.style || fileConfig.output?.style || baseConfig.output.style; baseConfig.output.filePath = defaultFilePathMap[style]; logger.trace('Default output file path is set to:', baseConfig.output.filePath); } const mergedConfig = { cwd, input: { ...baseConfig.input, ...fileConfig.input, ...cliConfig.input, }, output: { ...baseConfig.output, ...fileConfig.output, ...cliConfig.output, }, include: [...(baseConfig.include || []), ...(fileConfig.include || []), ...(cliConfig.include || [])], ignore: { ...baseConfig.ignore, ...fileConfig.ignore, ...cliConfig.ignore, customPatterns: [ ...(baseConfig.ignore.customPatterns || []), ...(fileConfig.ignore?.customPatterns || []), ...(cliConfig.ignore?.customPatterns || []), ], }, security: { ...baseConfig.security, ...fileConfig.security, ...cliConfig.security, }, }; try { return repomixConfigMergedSchema.parse(mergedConfig); } catch (error) { rethrowValidationErrorIfZodError(error, 'Invalid merged config'); throw error; } };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yamadashy/repomix'

If you have feedback or need assistance with the MCP directory API, please join our Discord server