index.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. 'use strict'
  2. var url = require('url')
  3. var gitHosts = require('./git-host-info.js')
  4. var GitHost = module.exports = require('./git-host.js')
  5. var protocolToRepresentationMap = {
  6. 'git+ssh:': 'sshurl',
  7. 'git+https:': 'https',
  8. 'ssh:': 'sshurl',
  9. 'git:': 'git'
  10. }
  11. function protocolToRepresentation (protocol) {
  12. return protocolToRepresentationMap[protocol] || protocol.slice(0, -1)
  13. }
  14. var authProtocols = {
  15. 'git:': true,
  16. 'https:': true,
  17. 'git+https:': true,
  18. 'http:': true,
  19. 'git+http:': true
  20. }
  21. var cache = {}
  22. module.exports.fromUrl = function (giturl, opts) {
  23. if (typeof giturl !== 'string') return
  24. var key = giturl + JSON.stringify(opts || {})
  25. if (!(key in cache)) {
  26. cache[key] = fromUrl(giturl, opts)
  27. }
  28. return cache[key]
  29. }
  30. function fromUrl (giturl, opts) {
  31. if (giturl == null || giturl === '') return
  32. var url = fixupUnqualifiedGist(
  33. isGitHubShorthand(giturl) ? 'github:' + giturl : giturl
  34. )
  35. var parsed = parseGitUrl(url)
  36. var shortcutMatch = url.match(new RegExp('^([^:]+):(?:(?:[^@:]+(?:[^@]+)?@)?([^/]*))[/](.+?)(?:[.]git)?($|#)'))
  37. var matches = Object.keys(gitHosts).map(function (gitHostName) {
  38. try {
  39. var gitHostInfo = gitHosts[gitHostName]
  40. var auth = null
  41. if (parsed.auth && authProtocols[parsed.protocol]) {
  42. auth = decodeURIComponent(parsed.auth)
  43. }
  44. var committish = parsed.hash ? decodeURIComponent(parsed.hash.substr(1)) : null
  45. var user = null
  46. var project = null
  47. var defaultRepresentation = null
  48. if (shortcutMatch && shortcutMatch[1] === gitHostName) {
  49. user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2])
  50. project = decodeURIComponent(shortcutMatch[3])
  51. defaultRepresentation = 'shortcut'
  52. } else {
  53. if (parsed.host && parsed.host !== gitHostInfo.domain && parsed.host.replace(/^www[.]/, '') !== gitHostInfo.domain) return
  54. if (!gitHostInfo.protocols_re.test(parsed.protocol)) return
  55. if (!parsed.path) return
  56. var pathmatch = gitHostInfo.pathmatch
  57. var matched = parsed.path.match(pathmatch)
  58. if (!matched) return
  59. if (matched[1] !== null && matched[1] !== undefined) {
  60. user = decodeURIComponent(matched[1].replace(/^:/, ''))
  61. }
  62. project = decodeURIComponent(matched[2])
  63. defaultRepresentation = protocolToRepresentation(parsed.protocol)
  64. }
  65. return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts)
  66. } catch (ex) {
  67. /* istanbul ignore else */
  68. if (ex instanceof URIError) {
  69. } else throw ex
  70. }
  71. }).filter(function (gitHostInfo) { return gitHostInfo })
  72. if (matches.length !== 1) return
  73. return matches[0]
  74. }
  75. function isGitHubShorthand (arg) {
  76. // Note: This does not fully test the git ref format.
  77. // See https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
  78. //
  79. // The only way to do this properly would be to shell out to
  80. // git-check-ref-format, and as this is a fast sync function,
  81. // we don't want to do that. Just let git fail if it turns
  82. // out that the commit-ish is invalid.
  83. // GH usernames cannot start with . or -
  84. return /^[^:@%/\s.-][^:@%/\s]*[/][^:@\s/%]+(?:#.*)?$/.test(arg)
  85. }
  86. function fixupUnqualifiedGist (giturl) {
  87. // necessary for round-tripping gists
  88. var parsed = url.parse(giturl)
  89. if (parsed.protocol === 'gist:' && parsed.host && !parsed.path) {
  90. return parsed.protocol + '/' + parsed.host
  91. } else {
  92. return giturl
  93. }
  94. }
  95. function parseGitUrl (giturl) {
  96. var matched = giturl.match(/^([^@]+)@([^:/]+):[/]?((?:[^/]+[/])?[^/]+?)(?:[.]git)?(#.*)?$/)
  97. if (!matched) return url.parse(giturl)
  98. return {
  99. protocol: 'git+ssh:',
  100. slashes: true,
  101. auth: matched[1],
  102. host: matched[2],
  103. port: null,
  104. hostname: matched[2],
  105. hash: matched[4],
  106. search: null,
  107. query: null,
  108. pathname: '/' + matched[3],
  109. path: '/' + matched[3],
  110. href: 'git+ssh://' + matched[1] + '@' + matched[2] +
  111. '/' + matched[3] + (matched[4] || '')
  112. }
  113. }