Files
aiturk-hermes-ide/apps/desktop/src/lib/plugin-source-urls.ts
T

120 lines
3.2 KiB
TypeScript

const GITHUB_BROWSER_SEGMENTS = new Set(['tree', 'blob', 'commit'])
export interface PluginSourceLinks {
gitUrl: string
browseUrl: string | null
subdir: string | null
}
function resolvePluginGitUrl(identifier: string): { gitUrl: string; subdir: string | null } {
const trimmed = identifier.trim()
if (!trimmed) {
throw new Error('Plugin identifier is required.')
}
if (/^(https?:\/\/|git@|ssh:\/\/|file:\/\/)/.test(trimmed)) {
if (trimmed.startsWith('https://github.com/')) {
const rest = trimmed.slice('https://github.com/'.length).split(/[?#]/)[0].replace(/\/+$/, '')
const parts = rest.split('/').filter(Boolean)
if (parts.length >= 3 && parts[2] && GITHUB_BROWSER_SEGMENTS.has(parts[2])) {
const repo = parts[1].replace(/\.git$/, '')
let subdir: string | null = null
if (parts[2] === 'tree' && parts.length >= 5) {
subdir = parts.slice(4).join('/').replace(/\/+$/, '') || null
}
return { gitUrl: `https://github.com/${parts[0]}/${repo}.git`, subdir }
}
}
if (trimmed.includes('#')) {
const hashIdx = trimmed.indexOf('#')
const gitUrl = trimmed.slice(0, hashIdx)
const subdir = trimmed.slice(hashIdx + 1).replace(/^\/+|\/+$/g, '') || null
return { gitUrl, subdir }
}
const marker = '.git/'
if (trimmed.includes(marker)) {
const idx = trimmed.indexOf(marker)
const gitUrl = trimmed.slice(0, idx + marker.length - 1)
const subdir = trimmed.slice(idx + marker.length).replace(/^\/+|\/+$/g, '') || null
return { gitUrl, subdir }
}
return { gitUrl: trimmed, subdir: null }
}
const parts = trimmed.split('/').filter(Boolean)
if (parts.length >= 2) {
const [owner, repo, ...rest] = parts
const gitUrl = `https://github.com/${owner}/${repo}.git`
const subdir = rest.join('/').replace(/\/+$/, '') || null
return { gitUrl, subdir }
}
throw new Error('Invalid plugin identifier.')
}
function githubBrowseBase(gitUrl: string): string | null {
const sshMatch = gitUrl.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/i)
if (sshMatch) {
return `https://github.com/${sshMatch[1]}/${sshMatch[2].replace(/\.git$/, '')}`
}
try {
const url = new URL(gitUrl)
if (url.hostname.toLowerCase() === 'github.com') {
const parts = url.pathname.replace(/\/+$/, '').split('/').filter(Boolean)
if (parts.length >= 2) {
return `https://github.com/${parts[0]}/${parts[1].replace(/\.git$/, '')}`
}
}
} catch {
return null
}
return null
}
function browseUrlFromGitUrl(gitUrl: string, subdir: string | null): string | null {
const githubBase = githubBrowseBase(gitUrl)
if (githubBase) {
return subdir ? `${githubBase}/tree/HEAD/${subdir}` : githubBase
}
if (/^https?:\/\//.test(gitUrl)) {
const base = gitUrl.replace(/\.git$/, '')
return subdir ? `${base}/tree/HEAD/${subdir}` : base
}
return null
}
export function resolvePluginSourceLinks(identifier: string): PluginSourceLinks | null {
try {
const { gitUrl, subdir } = resolvePluginGitUrl(identifier)
return {
gitUrl,
browseUrl: browseUrlFromGitUrl(gitUrl, subdir),
subdir
}
} catch {
return null
}
}