text: 'use strict'
  var url = require('url')
  var gitHosts = require('./git-host-info.js')
  var GitHost = module.exports = require('./git-host.js')
  var LRU = require('lru-cache')
  var cache = new LRU({max: 1000})
  
  var protocolToRepresentationMap = {
    'git+ssh:': 'sshurl',
    'git+https:': 'https',
    'ssh:': 'sshurl',
    'git:': 'git'
  }
  
  var OTHER_HOSTS = 'otherHosts'
  
  function protocolToRepresentation (protocol) {
    return protocolToRepresentationMap[protocol] || protocol.slice(0, -1)
  }
  
  var authProtocols = {
    'git:': true,
    'https:': true,
    'git+https:': true,
    'http:': true,
    'git+http:': true
  }
  
  module.exports.fromUrl = function (giturl, opts) {
    if (typeof giturl !== 'string') return
    var key = giturl + JSON.stringify(opts || {})
  
    if (!cache.has(key)) {
      cache.set(key, fromUrl(giturl, opts))
    }
  
    return cache.get(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)?($|#)'))
  
    function matcher (gitHostName, allowOtherHosts) {
      if (!allowOtherHosts && gitHostName === OTHER_HOSTS) return
      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
        var domain = null
        if (shortcutMatch && shortcutMatch[1] === gitHostName) {
          user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2])
          project = decodeURIComponent(shortcutMatch[3])
          defaultRepresentation = 'shortcut'
        } else {
          if (!allowOtherHosts && 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])
          domain = parsed.host
          defaultRepresentation = protocolToRepresentation(parsed.protocol)
        }
        return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts, domain)
      } catch (ex) {
        /* istanbul ignore else */
        if (ex instanceof URIError) {
        } else throw ex
      }
    }
  
    // Do not optimize - first try known hosts
    var matches = Object.keys(gitHosts)
      .map(function (gitHostName) { return matcher(gitHostName) })
      .filter(function (gitHostInfo) { return gitHostInfo })
  
    // second try with any host
    if (opts && opts.allowOtherHosts && !matches.length) {
      return matcher(OTHER_HOSTS, true)
    }
  
    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 (legacy.auth) {
        // 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.
        const 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] || '')
    }
  }
  