You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
5.0 KiB
148 lines
5.0 KiB
'use strict' |
|
var url = require('url') |
|
var gitHosts = require('./git-host-info.js') |
|
var GitHost = module.exports = require('./git-host.js') |
|
|
|
var protocolToRepresentationMap = { |
|
'git+ssh:': 'sshurl', |
|
'git+https:': 'https', |
|
'ssh:': 'sshurl', |
|
'git:': 'git' |
|
} |
|
|
|
function protocolToRepresentation (protocol) { |
|
return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) |
|
} |
|
|
|
var authProtocols = { |
|
'git:': true, |
|
'https:': true, |
|
'git+https:': true, |
|
'http:': true, |
|
'git+http:': true |
|
} |
|
|
|
var cache = {} |
|
|
|
module.exports.fromUrl = function (giturl, opts) { |
|
if (typeof giturl !== 'string') return |
|
var key = giturl + JSON.stringify(opts || {}) |
|
|
|
if (!(key in cache)) { |
|
cache[key] = fromUrl(giturl, opts) |
|
} |
|
|
|
return cache[key] |
|
} |
|
|
|
function fromUrl (giturl, opts) { |
|
if (giturl == null || giturl === '') return |
|
var url = fixupUnqualifiedGist( |
|
isGitHubShorthand(giturl) ? 'github:' + giturl : giturl |
|
) |
|
var parsed = parseGitUrl(url) |
|
var shortcutMatch = url.match(/^([^:]+):(?:[^@]+@)?(?:([^/]*)\/)?([^#]+)/) |
|
var matches = Object.keys(gitHosts).map(function (gitHostName) { |
|
try { |
|
var gitHostInfo = gitHosts[gitHostName] |
|
var auth = null |
|
if (parsed.auth && authProtocols[parsed.protocol]) { |
|
auth = parsed.auth |
|
} |
|
var committish = parsed.hash ? decodeURIComponent(parsed.hash.substr(1)) : null |
|
var user = null |
|
var project = null |
|
var defaultRepresentation = null |
|
if (shortcutMatch && shortcutMatch[1] === gitHostName) { |
|
user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2]) |
|
project = decodeURIComponent(shortcutMatch[3].replace(/\.git$/, '')) |
|
defaultRepresentation = 'shortcut' |
|
} else { |
|
if (parsed.host && parsed.host !== gitHostInfo.domain && parsed.host.replace(/^www[.]/, '') !== gitHostInfo.domain) return |
|
if (!gitHostInfo.protocols_re.test(parsed.protocol)) return |
|
if (!parsed.path) return |
|
var pathmatch = gitHostInfo.pathmatch |
|
var matched = parsed.path.match(pathmatch) |
|
if (!matched) return |
|
/* istanbul ignore else */ |
|
if (matched[1] !== null && matched[1] !== undefined) { |
|
user = decodeURIComponent(matched[1].replace(/^:/, '')) |
|
} |
|
project = decodeURIComponent(matched[2]) |
|
defaultRepresentation = protocolToRepresentation(parsed.protocol) |
|
} |
|
return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) |
|
} catch (ex) { |
|
/* istanbul ignore else */ |
|
if (ex instanceof URIError) { |
|
} else throw ex |
|
} |
|
}).filter(function (gitHostInfo) { return gitHostInfo }) |
|
if (matches.length !== 1) return |
|
return matches[0] |
|
} |
|
|
|
function isGitHubShorthand (arg) { |
|
// Note: This does not fully test the git ref format. |
|
// See https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html |
|
// |
|
// The only way to do this properly would be to shell out to |
|
// git-check-ref-format, and as this is a fast sync function, |
|
// we don't want to do that. Just let git fail if it turns |
|
// out that the commit-ish is invalid. |
|
// GH usernames cannot start with . or - |
|
return /^[^:@%/\s.-][^:@%/\s]*[/][^:@\s/%]+(?:#.*)?$/.test(arg) |
|
} |
|
|
|
function fixupUnqualifiedGist (giturl) { |
|
// necessary for round-tripping gists |
|
var parsed = url.parse(giturl) |
|
if (parsed.protocol === 'gist:' && parsed.host && !parsed.path) { |
|
return parsed.protocol + '/' + parsed.host |
|
} else { |
|
return giturl |
|
} |
|
} |
|
|
|
function parseGitUrl (giturl) { |
|
var matched = giturl.match(/^([^@]+)@([^:/]+):[/]?((?:[^/]+[/])?[^/]+?)(?:[.]git)?(#.*)?$/) |
|
if (!matched) { |
|
var legacy = url.parse(giturl) |
|
// If we don't have url.URL, then sorry, this is just not fixable. |
|
// This affects Node <= 6.12. |
|
if (legacy.auth && typeof url.URL === 'function') { |
|
// git urls can be in the form of scp-style/ssh-connect strings, like |
|
// git+ssh://user@host.com:some/path, which the legacy url parser |
|
// supports, but WhatWG url.URL class does not. However, the legacy |
|
// parser de-urlencodes the username and password, so something like |
|
// https://user%3An%40me:p%40ss%3Aword@x.com/ becomes |
|
// https://user:n@me:p@ss:word@x.com/ which is all kinds of wrong. |
|
// Pull off just the auth and host, so we dont' get the confusing |
|
// scp-style URL, then pass that to the WhatWG parser to get the |
|
// auth properly escaped. |
|
var authmatch = giturl.match(/[^@]+@[^:/]+/) |
|
/* istanbul ignore else - this should be impossible */ |
|
if (authmatch) { |
|
var whatwg = new url.URL(authmatch[0]) |
|
legacy.auth = whatwg.username || '' |
|
if (whatwg.password) legacy.auth += ':' + whatwg.password |
|
} |
|
} |
|
return legacy |
|
} |
|
return { |
|
protocol: 'git+ssh:', |
|
slashes: true, |
|
auth: matched[1], |
|
host: matched[2], |
|
port: null, |
|
hostname: matched[2], |
|
hash: matched[4], |
|
search: null, |
|
query: null, |
|
pathname: '/' + matched[3], |
|
path: '/' + matched[3], |
|
href: 'git+ssh://' + matched[1] + '@' + matched[2] + |
|
'/' + matched[3] + (matched[4] || '') |
|
} |
|
}
|
|
|