Skip to main content
Glama
freefish1218

MCP HuggingFetch

by freefish1218
patterns.js7.79 kB
/** * 模式匹配模块 - 基于 micromatch 的统一 glob 处理 */ const micromatch = require('micromatch'); const path = require('path'); /** * 匹配单个文件路径是否符合模式 * @param {string} filePath - 文件路径 * @param {string|string[]} pattern - glob 模式,可以是字符串或数组 * @returns {boolean} 是否匹配 */ function matchPattern(filePath, pattern) { if (!pattern) { return true; // 没有模式时匹配所有 } // 统一处理路径分隔符 const normalizedPath = filePath.replace(/\\/g, '/'); // 单个模式 if (typeof pattern === 'string') { // 如果模式不包含路径分隔符且不包含通配符,则匹配文件名 if (!pattern.includes('/') && !pattern.includes('*') && !pattern.includes('?')) { // 精确文件名匹配 - config.json 应该匹配 path/to/config.json return normalizedPath.endsWith('/' + pattern) || normalizedPath === pattern; } return micromatch.isMatch(normalizedPath, pattern); } // 多个模式(数组) if (Array.isArray(pattern)) { return pattern.some(p => matchPattern(filePath, p)); } return true; } /** * 应用文件过滤器 * @param {string[]} files - 文件路径数组 * @param {object} filter - 过滤器选项 * @param {string|string[]} filter.include - 包含模式(之前的 allow_patterns) * @param {string|string[]} filter.exclude - 排除模式(之前的 ignore_patterns) * @returns {string[]} 过滤后的文件列表 */ function applyFilter(files, filter = {}) { if (!filter || (!filter.include && !filter.exclude)) { return files; } let filtered = files.map(f => f.replace(/\\/g, '/')); // 标准化路径 // 应用包含模式 if (filter.include) { const patterns = Array.isArray(filter.include) ? filter.include : [filter.include]; filtered = micromatch(filtered, patterns); } // 应用排除模式 if (filter.exclude) { const patterns = Array.isArray(filter.exclude) ? filter.exclude : [filter.exclude]; filtered = micromatch.not(filtered, patterns); } return filtered; } /** * 检查文件是否应该被忽略 * @param {string} filePath - 文件路径 * @param {string|string[]} ignorePatterns - 忽略模式 * @returns {boolean} 是否应该被忽略 */ function shouldIgnore(filePath, ignorePatterns) { if (!ignorePatterns) { return false; } const normalizedPath = filePath.replace(/\\/g, '/'); const patterns = Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns]; return micromatch.isMatch(normalizedPath, patterns); } /** * 将文件列表按模式分组 * @param {string[]} files - 文件列表 * @param {object} patterns - 模式映射 { key: pattern } * @returns {object} 分组后的文件 { key: files[] } */ function groupByPattern(files, patterns) { const groups = {}; for (const [key, pattern] of Object.entries(patterns)) { groups[key] = files.filter(file => matchPattern(file, pattern)); } // 添加未匹配的文件 const allMatched = new Set(); Object.values(groups).forEach(group => { group.forEach(file => allMatched.add(file)); }); groups.unmatched = files.filter(file => !allMatched.has(file)); return groups; } /** * 验证 glob 模式是否有效 * @param {string} pattern - glob 模式 * @returns {object} { valid: boolean, error?: string } */ function validatePattern(pattern) { try { // 尝试使用模式进行匹配测试 micromatch.isMatch('test', pattern); return { valid: true }; } catch (error) { return { valid: false, error: `无效的 glob 模式: ${error.message}` }; } } /** * 转换旧版模式参数到新格式 * @param {object} options - 旧版选项 * @returns {object} 新版过滤器格式 */ function migratePatterns(options) { const filter = {}; // 如果已经是新格式,直接使用 if (options.include || options.exclude) { filter.include = options.include; filter.exclude = options.exclude; return filter; } // 处理包含模式(旧格式) if (options.allow_patterns) { filter.include = options.allow_patterns; } else if (options.pattern) { filter.include = options.pattern; } else if (options.files && Array.isArray(options.files)) { // 精确匹配文件列表 filter.include = options.files; } // 处理排除模式(旧格式) if (options.ignore_patterns) { filter.exclude = options.ignore_patterns; } return filter; } /** * 根据文件扩展名分类 * @param {string[]} files - 文件列表 * @returns {object} 按扩展名分类的文件 */ function groupByExtension(files) { const groups = {}; files.forEach(file => { const ext = path.extname(file).toLowerCase() || 'no-extension'; if (!groups[ext]) { groups[ext] = []; } groups[ext].push(file); }); return groups; } /** * 常用的预定义模式 */ const PRESET_PATTERNS = { // 模型文件 models: ['*.safetensors', '*.bin', '*.pt', '*.pth', '*.onnx', '*.ckpt', '*.h5'], // 配置文件 configs: ['*.json', '*.yaml', '*.yml', '*.toml', 'config.*'], // 文档文件 docs: ['*.md', '*.txt', '*.rst', 'README*', 'LICENSE*'], // 代码文件 code: ['*.py', '*.js', '*.ts', '*.jsx', '*.tsx', '*.java', '*.cpp', '*.c'], // 数据文件 data: ['*.csv', '*.tsv', '*.jsonl', '*.parquet', '*.arrow'], // 媒体文件 media: ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.mp4', '*.mp3', '*.wav'], // 压缩文件 archives: ['*.zip', '*.tar', '*.tar.gz', '*.tgz', '*.tar.bz2', '*.rar', '*.7z'] }; /** * 获取文件类型(基于预定义模式) * @param {string} filePath - 文件路径 * @returns {string} 文件类型 */ function getFileType(filePath) { for (const [type, patterns] of Object.entries(PRESET_PATTERNS)) { if (matchPattern(filePath, patterns)) { return type; } } return 'other'; } /** * 创建文件过滤器 * @param {object} options - 过滤选项 * @returns {function} 过滤函数 */ function createFileFilter(options = {}) { const filter = migratePatterns(options); return (files) => { let result = Array.isArray(files) ? files : [files]; // 应用包含模式 if (filter.include) { result = result.filter(file => matchPattern(file, filter.include)); } // 应用排除模式 if (filter.exclude) { result = result.filter(file => !matchPattern(file, filter.exclude)); } // 应用大小过滤 if (options.maxSize || options.minSize) { result = result.filter(file => { if (file.size === undefined) return true; if (options.maxSize && file.size > options.maxSize) return false; if (options.minSize && file.size < options.minSize) return false; return true; }); } // 应用类型过滤 if (options.types && Array.isArray(options.types)) { result = result.filter(file => { const type = getFileType(typeof file === 'string' ? file : file.path); return options.types.includes(type); }); } return result; }; } /** * 扩展 glob 模式以包含所有子目录 * @param {string|string[]} pattern - 原始模式 * @returns {string|string[]} 扩展后的模式 */ function expandPattern(pattern) { if (typeof pattern === 'string') { // 如果模式不包含 **,添加它 if (!pattern.includes('**')) { return `**/${pattern}`; } return pattern; } if (Array.isArray(pattern)) { return pattern.map(p => expandPattern(p)); } return pattern; } module.exports = { matchPattern, applyFilter, shouldIgnore, groupByPattern, validatePattern, migratePatterns, groupByExtension, getFileType, createFileFilter, expandPattern, PRESET_PATTERNS };

Latest Blog Posts

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/freefish1218/mcp-huggingfetch'

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