index.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. "use strict";
  2. var gitUp = require("git-up");
  3. /**
  4. * gitUrlParse
  5. * Parses a Git url.
  6. *
  7. * @name gitUrlParse
  8. * @function
  9. * @param {String} url The Git url to parse.
  10. * @return {GitUrl} The `GitUrl` object containing:
  11. *
  12. * - `protocols` (Array): An array with the url protocols (usually it has one element).
  13. * - `port` (null|Number): The domain port.
  14. * - `resource` (String): The url domain (including subdomains).
  15. * - `user` (String): The authentication user (usually for ssh urls).
  16. * - `pathname` (String): The url pathname.
  17. * - `hash` (String): The url hash.
  18. * - `search` (String): The url querystring value.
  19. * - `href` (String): The input url.
  20. * - `protocol` (String): The git url protocol.
  21. * - `token` (String): The oauth token (could appear in the https urls).
  22. * - `source` (String): The Git provider (e.g. `"github.com"`).
  23. * - `owner` (String): The repository owner.
  24. * - `name` (String): The repository name.
  25. * - `ref` (String): The repository ref (e.g., "master" or "dev").
  26. * - `filepath` (String): A filepath relative to the repository root.
  27. * - `filepathtype` (String): The type of filepath in the url ("blob" or "tree").
  28. * - `full_name` (String): The owner and name values in the `owner/name` format.
  29. * - `toString` (Function): A function to stringify the parsed url into another url type.
  30. * - `organization` (String): The organization the owner belongs to. This is CloudForge specific.
  31. * - `git_suffix` (Boolean): Whether to add the `.git` suffix or not.
  32. *
  33. */
  34. function gitUrlParse(url) {
  35. if (typeof url !== "string") {
  36. throw new Error("The url must be a string.");
  37. }
  38. var urlInfo = gitUp(url),
  39. sourceParts = urlInfo.resource.split("."),
  40. splits = null;
  41. urlInfo.toString = function (type) {
  42. return gitUrlParse.stringify(this, type);
  43. };
  44. urlInfo.source = sourceParts.length > 2 ? sourceParts.slice(1 - sourceParts.length).join(".") : urlInfo.source = urlInfo.resource;
  45. // Note: Some hosting services (e.g. Visual Studio Team Services) allow whitespace characters
  46. // in the repository and owner names so we decode the URL pieces to get the correct result
  47. urlInfo.git_suffix = /\.git$/.test(urlInfo.pathname);
  48. urlInfo.name = decodeURIComponent(urlInfo.pathname.replace(/^\//, '').replace(/\.git$/, ""));
  49. urlInfo.owner = decodeURIComponent(urlInfo.user);
  50. switch (urlInfo.source) {
  51. case "git.cloudforge.com":
  52. urlInfo.owner = urlInfo.user;
  53. urlInfo.organization = sourceParts[0];
  54. urlInfo.source = "cloudforge.com";
  55. break;
  56. case "visualstudio.com":
  57. // Handle VSTS SSH URLs
  58. if (urlInfo.resource === 'vs-ssh.visualstudio.com') {
  59. splits = urlInfo.name.split("/");
  60. if (splits.length === 4) {
  61. urlInfo.organization = splits[1];
  62. urlInfo.owner = splits[2];
  63. urlInfo.name = splits[3];
  64. urlInfo.full_name = splits[2] + '/' + splits[3];
  65. }
  66. break;
  67. } else {
  68. splits = urlInfo.name.split("/");
  69. if (splits.length === 2) {
  70. urlInfo.owner = splits[1];
  71. urlInfo.name = splits[1];
  72. urlInfo.full_name = '_git/' + urlInfo.name;
  73. } else if (splits.length === 3) {
  74. urlInfo.name = splits[2];
  75. if (splits[0] === 'DefaultCollection') {
  76. urlInfo.owner = splits[2];
  77. urlInfo.organization = splits[0];
  78. urlInfo.full_name = urlInfo.organization + '/_git/' + urlInfo.name;
  79. } else {
  80. urlInfo.owner = splits[0];
  81. urlInfo.full_name = urlInfo.owner + '/_git/' + urlInfo.name;
  82. }
  83. } else if (splits.length === 4) {
  84. urlInfo.organization = splits[0];
  85. urlInfo.owner = splits[1];
  86. urlInfo.name = splits[3];
  87. urlInfo.full_name = urlInfo.organization + '/' + urlInfo.owner + '/_git/' + urlInfo.name;
  88. }
  89. break;
  90. }
  91. // Azure DevOps (formerly Visual Studio Team Services)
  92. case "dev.azure.com":
  93. case "azure.com":
  94. if (urlInfo.resource === 'ssh.dev.azure.com') {
  95. splits = urlInfo.name.split("/");
  96. if (splits.length === 4) {
  97. urlInfo.organization = splits[1];
  98. urlInfo.owner = splits[2];
  99. urlInfo.name = splits[3];
  100. }
  101. break;
  102. } else {
  103. splits = urlInfo.name.split("/");
  104. if (splits.length === 5) {
  105. urlInfo.organization = splits[0];
  106. urlInfo.owner = splits[1];
  107. urlInfo.name = splits[4];
  108. urlInfo.full_name = '_git/' + urlInfo.name;
  109. } else if (splits.length === 3) {
  110. urlInfo.name = splits[2];
  111. if (splits[0] === 'DefaultCollection') {
  112. urlInfo.owner = splits[2];
  113. urlInfo.organization = splits[0];
  114. urlInfo.full_name = urlInfo.organization + '/_git/' + urlInfo.name;
  115. } else {
  116. urlInfo.owner = splits[0];
  117. urlInfo.full_name = urlInfo.owner + '/_git/' + urlInfo.name;
  118. }
  119. } else if (splits.length === 4) {
  120. urlInfo.organization = splits[0];
  121. urlInfo.owner = splits[1];
  122. urlInfo.name = splits[3];
  123. urlInfo.full_name = urlInfo.organization + '/' + urlInfo.owner + '/_git/' + urlInfo.name;
  124. }
  125. break;
  126. }
  127. default:
  128. splits = urlInfo.name.split("/");
  129. var nameIndex = splits.length - 1;
  130. if (splits.length >= 2) {
  131. var blobIndex = splits.indexOf("blob", 2);
  132. var treeIndex = splits.indexOf("tree", 2);
  133. var commitIndex = splits.indexOf("commit", 2);
  134. nameIndex = blobIndex > 0 ? blobIndex - 1 : treeIndex > 0 ? treeIndex - 1 : commitIndex > 0 ? commitIndex - 1 : nameIndex;
  135. urlInfo.owner = splits.slice(0, nameIndex).join('/');
  136. urlInfo.name = splits[nameIndex];
  137. if (commitIndex) {
  138. urlInfo.commit = splits[nameIndex + 2];
  139. }
  140. }
  141. urlInfo.ref = "";
  142. urlInfo.filepathtype = "";
  143. urlInfo.filepath = "";
  144. if (splits.length > nameIndex + 2 && ["blob", "tree"].indexOf(splits[nameIndex + 1]) >= 0) {
  145. urlInfo.filepathtype = splits[nameIndex + 1];
  146. urlInfo.ref = splits[nameIndex + 2];
  147. if (splits.length > nameIndex + 3) {
  148. urlInfo.filepath = splits.slice(nameIndex + 3).join('/');
  149. }
  150. }
  151. urlInfo.organization = urlInfo.owner;
  152. break;
  153. }
  154. if (!urlInfo.full_name) {
  155. urlInfo.full_name = urlInfo.owner;
  156. if (urlInfo.name) {
  157. urlInfo.full_name && (urlInfo.full_name += "/");
  158. urlInfo.full_name += urlInfo.name;
  159. }
  160. }
  161. return urlInfo;
  162. }
  163. /**
  164. * stringify
  165. * Stringifies a `GitUrl` object.
  166. *
  167. * @name stringify
  168. * @function
  169. * @param {GitUrl} obj The parsed Git url object.
  170. * @param {String} type The type of the stringified url (default `obj.protocol`).
  171. * @return {String} The stringified url.
  172. */
  173. gitUrlParse.stringify = function (obj, type) {
  174. type = type || (obj.protocols && obj.protocols.length ? obj.protocols.join('+') : obj.protocol);
  175. var port = obj.port ? ":" + obj.port : '';
  176. var user = obj.user || 'git';
  177. var maybeGitSuffix = obj.git_suffix ? ".git" : "";
  178. switch (type) {
  179. case "ssh":
  180. if (port) return "ssh://" + user + "@" + obj.resource + port + "/" + obj.full_name + maybeGitSuffix;else return user + "@" + obj.resource + ":" + obj.full_name + maybeGitSuffix;
  181. case "git+ssh":
  182. case "ssh+git":
  183. case "ftp":
  184. case "ftps":
  185. return type + "://" + user + "@" + obj.resource + port + "/" + obj.full_name + maybeGitSuffix;
  186. case "http":
  187. case "https":
  188. var auth = obj.token ? buildToken(obj) : obj.user && (obj.protocols.includes('http') || obj.protocols.includes('https')) ? obj.user + "@" : "";
  189. return type + "://" + auth + obj.resource + port + "/" + obj.full_name + maybeGitSuffix;
  190. default:
  191. return obj.href;
  192. }
  193. };
  194. /*!
  195. * buildToken
  196. * Builds OAuth token prefix (helper function)
  197. *
  198. * @name buildToken
  199. * @function
  200. * @param {GitUrl} obj The parsed Git url object.
  201. * @return {String} token prefix
  202. */
  203. function buildToken(obj) {
  204. switch (obj.source) {
  205. case "bitbucket.org":
  206. return "x-token-auth:" + obj.token + "@";
  207. default:
  208. return obj.token + "@";
  209. }
  210. }
  211. module.exports = gitUrlParse;