| '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(new RegExp('^([^:]+):(?:(?:[^@:]+(?:[^@]+)?@)?([^/]*))[/](.+?)(?:[.]git)?($|#)')) |
| var matches = Object.keys(gitHosts).map(function (gitHostName) { |
| try { |
| var gitHostInfo = gitHosts[gitHostName] |
| var auth = null |
| if (parsed.auth && authProtocols[parsed.protocol]) { |
| auth = decodeURIComponent(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]) |
| 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) return url.parse(giturl) |
| 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] || '') |
| } |
| } |