UNPKG

repomix

Version:

A tool to pack repository contents to single file for AI consumption

134 lines (133 loc) 4.52 kB
import gitUrlParse, {} from 'git-url-parse'; import { RepomixError } from '../../shared/errorHandle.js'; import { logger } from '../../shared/logger.js'; const VALID_NAME_PATTERN = '[a-zA-Z0-9](?:[a-zA-Z0-9._-]*[a-zA-Z0-9])?'; const validShorthandRegex = new RegExp(`^${VALID_NAME_PATTERN}/${VALID_NAME_PATTERN}$`); export const isValidShorthand = (remoteValue) => { return validShorthandRegex.test(remoteValue); }; const isAzureDevOpsUrl = (remoteValue) => { if (remoteValue.startsWith('git@ssh.dev.azure.com:')) { return true; } try { const url = new URL(remoteValue); const hostname = url.hostname.toLowerCase(); if (hostname === 'dev.azure.com' || hostname === 'ssh.dev.azure.com') { return true; } if (hostname.endsWith('.visualstudio.com')) { return true; } return false; } catch { return false; } }; export const parseRemoteValue = (remoteValue, refs = []) => { if (isValidShorthand(remoteValue)) { logger.trace(`Formatting GitHub shorthand: ${remoteValue}`); return { repoUrl: `https://github.com/${remoteValue}.git`, remoteBranch: undefined, }; } if (isAzureDevOpsUrl(remoteValue)) { return { repoUrl: remoteValue, remoteBranch: undefined, }; } try { const parsedFields = gitUrlParse(remoteValue, refs); parsedFields.git_suffix = true; const ownerSlashRepo = parsedFields.full_name.split('/').length > 1 ? parsedFields.full_name.split('/').slice(-2).join('/') : ''; if (ownerSlashRepo !== '' && !isValidShorthand(ownerSlashRepo)) { throw new RepomixError('Invalid owner/repo in repo URL'); } const repoUrl = parsedFields.toString(parsedFields.protocol); if (parsedFields.ref) { return { repoUrl: repoUrl, remoteBranch: parsedFields.ref, }; } if (parsedFields.commit) { return { repoUrl: repoUrl, remoteBranch: parsedFields.commit, }; } return { repoUrl: repoUrl, remoteBranch: undefined, }; } catch { throw new RepomixError('Invalid remote repository URL or repository shorthand (owner/repo)'); } }; export { isExplicitRemoteUrl } from './gitRemoteUrl.js'; export const isValidRemoteValue = (remoteValue, refs = []) => { try { parseRemoteValue(remoteValue, refs); return true; } catch { return false; } }; export const parseGitHubRepoInfo = (remoteValue) => { try { if (isValidShorthand(remoteValue)) { const [owner, repo] = remoteValue.split('/'); return { owner, repo }; } try { const url = new URL(remoteValue); const allowedHosts = ['github.com', 'www.github.com']; if (allowedHosts.includes(url.hostname)) { const pathParts = url.pathname.split('/').filter(Boolean); if (pathParts.length >= 2) { const owner = pathParts[0]; const repo = pathParts[1].replace(/\.git$/, ''); const result = { owner, repo }; if (pathParts.length >= 4 && (pathParts[2] === 'tree' || pathParts[2] === 'commit')) { result.ref = pathParts.slice(3).join('/'); } return result; } } } catch (urlError) { logger.trace('URL parsing failed, falling back to git-url-parse:', urlError.message); } const parsed = gitUrlParse(remoteValue); if (parsed.source !== 'github.com') { return null; } const [owner, repo] = parsed.full_name.split('/'); if (!owner || !repo) { return null; } const result = { owner, repo: repo.replace(/\.git$/, ''), }; if (parsed.ref) { result.ref = parsed.ref; } else if (parsed.commit) { result.ref = parsed.commit; } return result; } catch (error) { logger.trace('Failed to parse GitHub repo info:', error.message); return null; } }; export const isGitHubRepository = (remoteValue) => { return parseGitHubRepoInfo(remoteValue) !== null; };