"use strict";
|
|
var gitUp = require("git-up");
|
|
/**
|
* gitUrlParse
|
* Parses a Git url.
|
*
|
* @name gitUrlParse
|
* @function
|
* @param {String} url The Git url to parse.
|
* @return {GitUrl} The `GitUrl` object containing:
|
*
|
* - `protocols` (Array): An array with the url protocols (usually it has one element).
|
* - `port` (null|Number): The domain port.
|
* - `resource` (String): The url domain (including subdomains).
|
* - `user` (String): The authentication user (usually for ssh urls).
|
* - `pathname` (String): The url pathname.
|
* - `hash` (String): The url hash.
|
* - `search` (String): The url querystring value.
|
* - `href` (String): The input url.
|
* - `protocol` (String): The git url protocol.
|
* - `token` (String): The oauth token (could appear in the https urls).
|
* - `source` (String): The Git provider (e.g. `"github.com"`).
|
* - `owner` (String): The repository owner.
|
* - `name` (String): The repository name.
|
* - `ref` (String): The repository ref (e.g., "master" or "dev").
|
* - `filepath` (String): A filepath relative to the repository root.
|
* - `filepathtype` (String): The type of filepath in the url ("blob" or "tree").
|
* - `full_name` (String): The owner and name values in the `owner/name` format.
|
* - `toString` (Function): A function to stringify the parsed url into another url type.
|
* - `organization` (String): The organization the owner belongs to. This is CloudForge specific.
|
* - `git_suffix` (Boolean): Whether to add the `.git` suffix or not.
|
*
|
*/
|
function gitUrlParse(url) {
|
|
if (typeof url !== "string") {
|
throw new Error("The url must be a string.");
|
}
|
|
var urlInfo = gitUp(url),
|
sourceParts = urlInfo.resource.split("."),
|
splits = null;
|
|
urlInfo.toString = function (type) {
|
return gitUrlParse.stringify(this, type);
|
};
|
|
urlInfo.source = sourceParts.length > 2 ? sourceParts.slice(1 - sourceParts.length).join(".") : urlInfo.source = urlInfo.resource;
|
|
// Note: Some hosting services (e.g. Visual Studio Team Services) allow whitespace characters
|
// in the repository and owner names so we decode the URL pieces to get the correct result
|
urlInfo.git_suffix = /\.git$/.test(urlInfo.pathname);
|
urlInfo.name = decodeURIComponent(urlInfo.pathname.replace(/^\//, '').replace(/\.git$/, ""));
|
urlInfo.owner = decodeURIComponent(urlInfo.user);
|
|
switch (urlInfo.source) {
|
case "git.cloudforge.com":
|
urlInfo.owner = urlInfo.user;
|
urlInfo.organization = sourceParts[0];
|
urlInfo.source = "cloudforge.com";
|
break;
|
case "visualstudio.com":
|
// Handle VSTS SSH URLs
|
if (urlInfo.resource === 'vs-ssh.visualstudio.com') {
|
splits = urlInfo.name.split("/");
|
if (splits.length === 4) {
|
urlInfo.organization = splits[1];
|
urlInfo.owner = splits[2];
|
urlInfo.name = splits[3];
|
urlInfo.full_name = splits[2] + '/' + splits[3];
|
}
|
break;
|
} else {
|
splits = urlInfo.name.split("/");
|
if (splits.length === 2) {
|
urlInfo.owner = splits[1];
|
urlInfo.name = splits[1];
|
urlInfo.full_name = '_git/' + urlInfo.name;
|
} else if (splits.length === 3) {
|
urlInfo.name = splits[2];
|
if (splits[0] === 'DefaultCollection') {
|
urlInfo.owner = splits[2];
|
urlInfo.organization = splits[0];
|
urlInfo.full_name = urlInfo.organization + '/_git/' + urlInfo.name;
|
} else {
|
urlInfo.owner = splits[0];
|
urlInfo.full_name = urlInfo.owner + '/_git/' + urlInfo.name;
|
}
|
} else if (splits.length === 4) {
|
urlInfo.organization = splits[0];
|
urlInfo.owner = splits[1];
|
urlInfo.name = splits[3];
|
urlInfo.full_name = urlInfo.organization + '/' + urlInfo.owner + '/_git/' + urlInfo.name;
|
}
|
break;
|
}
|
|
// Azure DevOps (formerly Visual Studio Team Services)
|
case "dev.azure.com":
|
case "azure.com":
|
if (urlInfo.resource === 'ssh.dev.azure.com') {
|
splits = urlInfo.name.split("/");
|
if (splits.length === 4) {
|
urlInfo.organization = splits[1];
|
urlInfo.owner = splits[2];
|
urlInfo.name = splits[3];
|
}
|
break;
|
} else {
|
splits = urlInfo.name.split("/");
|
if (splits.length === 5) {
|
urlInfo.organization = splits[0];
|
urlInfo.owner = splits[1];
|
urlInfo.name = splits[4];
|
urlInfo.full_name = '_git/' + urlInfo.name;
|
} else if (splits.length === 3) {
|
urlInfo.name = splits[2];
|
if (splits[0] === 'DefaultCollection') {
|
urlInfo.owner = splits[2];
|
urlInfo.organization = splits[0];
|
urlInfo.full_name = urlInfo.organization + '/_git/' + urlInfo.name;
|
} else {
|
urlInfo.owner = splits[0];
|
urlInfo.full_name = urlInfo.owner + '/_git/' + urlInfo.name;
|
}
|
} else if (splits.length === 4) {
|
urlInfo.organization = splits[0];
|
urlInfo.owner = splits[1];
|
urlInfo.name = splits[3];
|
urlInfo.full_name = urlInfo.organization + '/' + urlInfo.owner + '/_git/' + urlInfo.name;
|
}
|
if (urlInfo.query && urlInfo.query['path']) {
|
urlInfo.filepath = urlInfo.query['path'].replace(/^\/+/g, ''); // Strip leading slash (/)
|
}
|
if (urlInfo.query && urlInfo.query['version']) {
|
// version=GB<branch>
|
urlInfo.ref = urlInfo.query['version'].replace(/^GB/, ''); // remove GB
|
}
|
break;
|
}
|
default:
|
splits = urlInfo.name.split("/");
|
var nameIndex = splits.length - 1;
|
if (splits.length >= 2) {
|
var dashIndex = splits.indexOf("-", 2);
|
var blobIndex = splits.indexOf("blob", 2);
|
var treeIndex = splits.indexOf("tree", 2);
|
var commitIndex = splits.indexOf("commit", 2);
|
var srcIndex = splits.indexOf("src", 2);
|
var rawIndex = splits.indexOf("raw", 2);
|
nameIndex = dashIndex > 0 ? dashIndex - 1 : blobIndex > 0 ? blobIndex - 1 : treeIndex > 0 ? treeIndex - 1 : commitIndex > 0 ? commitIndex - 1 : srcIndex > 0 ? srcIndex - 1 : rawIndex > 0 ? rawIndex - 1 : nameIndex;
|
|
urlInfo.owner = splits.slice(0, nameIndex).join('/');
|
urlInfo.name = splits[nameIndex];
|
if (commitIndex) {
|
urlInfo.commit = splits[nameIndex + 2];
|
}
|
}
|
|
urlInfo.ref = "";
|
urlInfo.filepathtype = "";
|
urlInfo.filepath = "";
|
var offsetNameIndex = splits.length > nameIndex && splits[nameIndex + 1] === "-" ? nameIndex + 1 : nameIndex;
|
if (splits.length > offsetNameIndex + 2 && ["raw", "src", "blob", "tree"].indexOf(splits[offsetNameIndex + 1]) >= 0) {
|
urlInfo.filepathtype = splits[offsetNameIndex + 1];
|
urlInfo.ref = splits[offsetNameIndex + 2];
|
if (splits.length > offsetNameIndex + 3) {
|
urlInfo.filepath = splits.slice(offsetNameIndex + 3).join('/');
|
}
|
}
|
urlInfo.organization = urlInfo.owner;
|
break;
|
}
|
|
if (!urlInfo.full_name) {
|
urlInfo.full_name = urlInfo.owner;
|
if (urlInfo.name) {
|
urlInfo.full_name && (urlInfo.full_name += "/");
|
urlInfo.full_name += urlInfo.name;
|
}
|
}
|
// Bitbucket Server
|
if (urlInfo.owner.startsWith("scm/")) {
|
urlInfo.source = "bitbucket-server";
|
urlInfo.owner = urlInfo.owner.replace("scm/", "");
|
urlInfo.organization = urlInfo.owner;
|
urlInfo.full_name = urlInfo.owner + "/" + urlInfo.name;
|
}
|
|
var bitbucket = /(projects|users)\/(.*?)\/repos\/(.*?)((\/.*$)|$)/;
|
var matches = bitbucket.exec(urlInfo.pathname);
|
if (matches != null) {
|
urlInfo.source = "bitbucket-server";
|
if (matches[1] === "users") {
|
urlInfo.owner = "~" + matches[2];
|
} else {
|
urlInfo.owner = matches[2];
|
}
|
|
urlInfo.organization = urlInfo.owner;
|
urlInfo.name = matches[3];
|
|
splits = matches[4].split("/");
|
if (splits.length > 1) {
|
if (["raw", "browse"].indexOf(splits[1]) >= 0) {
|
urlInfo.filepathtype = splits[1];
|
if (splits.length > 2) {
|
urlInfo.filepath = splits.slice(2).join('/');
|
}
|
} else if (splits[1] === "commits" && splits.length > 2) {
|
urlInfo.commit = splits[2];
|
}
|
}
|
urlInfo.full_name = urlInfo.owner + "/" + urlInfo.name;
|
|
if (urlInfo.query.at) {
|
urlInfo.ref = urlInfo.query.at;
|
} else {
|
urlInfo.ref = "";
|
}
|
}
|
return urlInfo;
|
}
|
|
/**
|
* stringify
|
* Stringifies a `GitUrl` object.
|
*
|
* @name stringify
|
* @function
|
* @param {GitUrl} obj The parsed Git url object.
|
* @param {String} type The type of the stringified url (default `obj.protocol`).
|
* @return {String} The stringified url.
|
*/
|
gitUrlParse.stringify = function (obj, type) {
|
type = type || (obj.protocols && obj.protocols.length ? obj.protocols.join('+') : obj.protocol);
|
var port = obj.port ? ":" + obj.port : '';
|
var user = obj.user || 'git';
|
var maybeGitSuffix = obj.git_suffix ? ".git" : "";
|
switch (type) {
|
case "ssh":
|
if (port) return "ssh://" + user + "@" + obj.resource + port + "/" + obj.full_name + maybeGitSuffix;else return user + "@" + obj.resource + ":" + obj.full_name + maybeGitSuffix;
|
case "git+ssh":
|
case "ssh+git":
|
case "ftp":
|
case "ftps":
|
return type + "://" + user + "@" + obj.resource + port + "/" + obj.full_name + maybeGitSuffix;
|
case "http":
|
case "https":
|
var auth = obj.token ? buildToken(obj) : obj.user && (obj.protocols.includes('http') || obj.protocols.includes('https')) ? obj.user + "@" : "";
|
return type + "://" + auth + obj.resource + port + "/" + buildPath(obj) + maybeGitSuffix;
|
default:
|
return obj.href;
|
}
|
};
|
|
/*!
|
* buildToken
|
* Builds OAuth token prefix (helper function)
|
*
|
* @name buildToken
|
* @function
|
* @param {GitUrl} obj The parsed Git url object.
|
* @return {String} token prefix
|
*/
|
function buildToken(obj) {
|
switch (obj.source) {
|
case "bitbucket.org":
|
return "x-token-auth:" + obj.token + "@";
|
default:
|
return obj.token + "@";
|
}
|
}
|
|
function buildPath(obj) {
|
switch (obj.source) {
|
case "bitbucket-server":
|
return "scm/" + obj.full_name;
|
default:
|
return "" + obj.full_name;
|
|
}
|
}
|
|
module.exports = gitUrlParse;
|