120 lines
3.2 KiB
TypeScript
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
|
|
}
|
|
}
|