type GitProvider = "github" | "gitlab" | "bitbucket" | "unknown";
interface RepoInfo {
provider: GitProvider;
host: string;
owner: string;
repo: string;
}
/**
* Parses a Git repository URL and extracts provider, owner, and repo.
*/
function parseRepoUrl(repoUrl: string): RepoInfo {
const url = new URL(repoUrl);
const host = url.host.toLowerCase();
const pathParts = url.pathname.split("/").filter(Boolean);
if (pathParts.length < 2) {
throw new Error(`Invalid repository URL: ${repoUrl}`);
}
const owner = pathParts[0];
const repo = pathParts[1].replace(/\.git$/, "");
let provider: GitProvider = "unknown";
if (host.includes("github")) {
provider = "github";
} else if (host.includes("gitlab")) {
provider = "gitlab";
} else if (host.includes("bitbucket")) {
provider = "bitbucket";
}
return { provider, host, owner, repo };
}
/**
* Builds raw file URLs for different Git providers.
*/
function buildRawUrls(info: RepoInfo, branch: string, filename: string): string[] {
const { provider, host, owner, repo } = info;
switch (provider) {
case "github":
return [`https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filename}`];
case "gitlab":
return [`https://${host}/${owner}/${repo}/-/raw/${branch}/${filename}`];
case "bitbucket":
return [`https://${host}/${owner}/${repo}/raw/${branch}/${filename}`];
default:
// Try common patterns for unknown providers
return [
`https://${host}/${owner}/${repo}/-/raw/${branch}/${filename}`,
`https://${host}/${owner}/${repo}/raw/${branch}/${filename}`,
`https://raw.${host}/${owner}/${repo}/${branch}/${filename}`,
];
}
}
/**
* Fetches a README from a Git repository URL.
* Supports GitHub, GitLab, Bitbucket, and attempts to handle unknown providers.
* @param repoUrl - Repository URL (e.g., https://github.com/user/repo)
* @returns The README content as a string
*/
export async function getReadmeFromGitRepo(repoUrl: string): Promise<string> {
const repoInfo = parseRepoUrl(repoUrl);
const branches = ["main", "master"];
const filenames = ["README.md", "readme.md", "Readme.md", "README.rst", "README"];
for (const branch of branches) {
for (const filename of filenames) {
const urls = buildRawUrls(repoInfo, branch, filename);
for (const url of urls) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.text();
}
} catch {
// Continue trying other combinations
}
}
}
}
throw new Error(`Could not find README in repository: ${repoUrl}`);
}