var jsReleases = require('node-releases/data/processed/envs.json') var agents = require('caniuse-lite/dist/unpacker/agents').agents var jsEOL = require('node-releases/data/release-schedule/release-schedule.json') var path = require('path') var e2c = require('electron-to-chromium/versions')

var BrowserslistError = require('./error') var env = require('./node') // Will load browser.js in webpack

var FLOAT_RANGE = /^d+(.d+)?(-d+(.d+)?)*$/ var YEAR = 365.259641 * 24 * 60 * 60 * 1000

var QUERY_OR = 1 var QUERY_AND = 2

function isVersionsMatch (versionA, versionB) {

return (versionA + '.').indexOf(versionB + '.') === 0

}

function isEolReleased (name) {

var version = name.slice(1)
return jsReleases.some(function (i) {
  return isVersionsMatch(i.version, version)
})

}

function normalize (versions) {

return versions.filter(function (version) {
  return typeof version === 'string'
})

}

function normalizeElectron (version) {

var versionToUse = version
if (version.split('.').length === 3) {
  versionToUse = version
    .split('.')
    .slice(0, -1)
    .join('.')
}
return versionToUse

}

function nameMapper (name) {

return function mapName (version) {
  return name + ' ' + version
}

}

function getMajor (version) {

return parseInt(version.split('.')[0])

}

function getMajorVersions (released, number) {

if (released.length === 0) return []
var minimum = getMajor(released[released.length - 1]) - parseInt(number) + 1
var selected = []
for (var i = released.length - 1; i >= 0; i--) {
  if (minimum > getMajor(released[i])) break
  selected.unshift(released[i])
}
return selected

}

function uniq (array) {

var filtered = []
for (var i = 0; i < array.length; i++) {
  if (filtered.indexOf(array[i]) === -1) filtered.push(array[i])
}
return filtered

}

// Helpers

function fillUsage (result, name, data) {

for (var i in data) {
  result[name + ' ' + i] = data[i]
}

}

function generateFilter (sign, version) {

version = parseFloat(version)
if (sign === '>') {
  return function (v) {
    return parseFloat(v) > version
  }
} else if (sign === '>=') {
  return function (v) {
    return parseFloat(v) >= version
  }
} else if (sign === '<') {
  return function (v) {
    return parseFloat(v) < version
  }
} else {
  return function (v) {
    return parseFloat(v) <= version
  }
}

}

function generateSemverFilter (sign, version) {

version = version.split('.').map(parseSimpleInt)
version[1] = version[1] || 0
version[2] = version[2] || 0
if (sign === '>') {
  return function (v) {
    v = v.split('.').map(parseSimpleInt)
    return compareSemver(v, version) > 0
  }
} else if (sign === '>=') {
  return function (v) {
    v = v.split('.').map(parseSimpleInt)
    return compareSemver(v, version) >= 0
  }
} else if (sign === '<') {
  return function (v) {
    v = v.split('.').map(parseSimpleInt)
    return compareSemver(version, v) > 0
  }
} else {
  return function (v) {
    v = v.split('.').map(parseSimpleInt)
    return compareSemver(version, v) >= 0
  }
}

}

function parseSimpleInt (x) {

return parseInt(x)

}

function compare (a, b) {

if (a < b) return -1
if (a > b) return +1
return 0

}

function compareSemver (a, b) {

return (
  compare(a[0], b[0]) ||
  compare(a[1], b[1]) ||
  compare(a[2], b[2])
)

}

function resolveVersion (data, version) {

if (data.versions.indexOf(version) !== -1) {
  return version
} else if (browserslist.versionAliases[data.name][version]) {
  return browserslist.versionAliases[data.name][version]
} else {
  return false
}

}

function normalizeVersion (data, version, context) {

var resolved = resolveVersion(data, version)
if (
  !resolved &&
  context.mobileToDesktop &&
  browserslist.desktopNames[data.name]
) {
  var alias = checkName(browserslist.desktopNames[data.name])
  resolved = resolveVersion(alias, version)
}
if (resolved) {
  return resolved
} else if (data.versions.length === 1) {
  return data.versions[0]
} else {
  return false
}

}

function filterByYear (since) {

since = since / 1000
return Object.keys(agents).reduce(function (selected, name) {
  var data = byName(name)
  if (!data) return selected
  var versions = Object.keys(data.releaseDate).filter(function (v) {
    return data.releaseDate[v] >= since
  })
  return selected.concat(versions.map(nameMapper(data.name)))
}, [])

}

function byName (name) {

name = name.toLowerCase()
name = browserslist.aliases[name] || name
return browserslist.data[name]

}

function checkName (name) {

var data = byName(name)
if (!data) throw new BrowserslistError('Unknown browser ' + name)
return data

}

function unknownQuery (query) {

return new BrowserslistError(
  'Unknown browser query `' + query + '`. ' +
  'Maybe you are using old Browserslist or made typo in query.'
)

}

function filterAndroid (list, versions) {

var released = browserslist.data.android.released
var firstEvergreen = 37
var last = released[released.length - 1]
var diff = last - firstEvergreen - versions // First Android Evergreen
if (diff > 0) {
  return list.slice(-1)
} else {
  return list.slice(diff - 1)
}

}

/**

* Resolves queries into a browser list.
* @param {string|string[]} queries Queries to combine.
* Either an array of queries or a long string of queries.
* @param {object} [context] Optional arguments to
* the select function in `queries`.
* @returns {string[]} A list of browsers
*/

function resolve (queries, context) {

if (Array.isArray(queries)) {
  queries = flatten(queries.map(parse))
} else {
  queries = parse(queries)
}

return queries.reduce(function (result, query, index) {
  var selection = query.queryString

  var isExclude = selection.indexOf('not ') === 0
  if (isExclude) {
    if (index === 0) {
      throw new BrowserslistError(
        'Write any browsers query (for instance, `defaults`) ' +
        'before `' + selection + '`')
    }
    selection = selection.slice(4)
  }

  for (var i = 0; i < QUERIES.length; i++) {
    var type = QUERIES[i]
    var match = selection.match(type.regexp)
    if (match) {
      var args = [context].concat(match.slice(1))
      var array = type.select.apply(browserslist, args).map(function (j) {
        var parts = j.split(' ')
        if (parts[1] === '0') {
          return parts[0] + ' ' + byName(parts[0]).versions[0]
        } else {
          return j
        }
      })

      switch (query.type) {
        case QUERY_AND:
          if (isExclude) {
            return result.filter(function (j) {
              return array.indexOf(j) === -1
            })
          } else {
            return result.filter(function (j) {
              return array.indexOf(j) !== -1
            })
          }
        case QUERY_OR:
        default:
          if (isExclude) {
            var filter = { }
            array.forEach(function (j) {
              filter[j] = true
            })
            return result.filter(function (j) {
              return !filter[j]
            })
          }
          return result.concat(array)
      }
    }
  }

  throw unknownQuery(selection)
}, [])

}

/**

* Return array of browsers by selection queries.
*
* @param {(string|string[])} [queries=browserslist.defaults] Browser queries.
* @param {object} [opts] Options.
* @param {string} [opts.path="."] Path to processed file.
*                                 It will be used to find config files.
* @param {string} [opts.env="production"] Processing environment.
*                                         It will be used to take right
*                                         queries from config file.
* @param {string} [opts.config] Path to config file with queries.
* @param {object} [opts.stats] Custom browser usage statistics
*                              for "> 1% in my stats" query.
* @param {boolean} [opts.ignoreUnknownVersions=false] Do not throw on unknown
*                                                     version in direct query.
* @param {boolean} [opts.dangerousExtend] Disable security checks
*                                         for extend query.
* @param {boolean} [opts.mobileToDesktop] Alias mobile browsers to the desktop
*                                         version when Can I Use doesn't have
*                                         data about the specified version.
* @returns {string[]} Array with browser names in Can I Use.
*
* @example
* browserslist('IE >= 10, IE 8') //=> ['ie 11', 'ie 10', 'ie 8']
*/

function browserslist (queries, opts) {

if (typeof opts === 'undefined') opts = { }

if (typeof opts.path === 'undefined') {
  opts.path = path.resolve ? path.resolve('.') : '.'
}

if (typeof queries === 'undefined' || queries === null) {
  var config = browserslist.loadConfig(opts)
  if (config) {
    queries = config
  } else {
    queries = browserslist.defaults
  }
}

if (!(typeof queries === 'string' || Array.isArray(queries))) {
  throw new BrowserslistError(
    'Browser queries must be an array or string. Got ' + typeof queries + '.')
}

var context = {
  ignoreUnknownVersions: opts.ignoreUnknownVersions,
  dangerousExtend: opts.dangerousExtend,
  mobileToDesktop: opts.mobileToDesktop
}

env.oldDataWarning(browserslist.data)
var stats = env.getStat(opts, browserslist.data)
if (stats) {
  context.customUsage = { }
  for (var browser in stats) {
    fillUsage(context.customUsage, browser, stats[browser])
  }
}

var result = resolve(queries, context).sort(function (name1, name2) {
  name1 = name1.split(' ')
  name2 = name2.split(' ')
  if (name1[0] === name2[0]) {
    if (FLOAT_RANGE.test(name1[1]) && FLOAT_RANGE.test(name2[1])) {
      return parseFloat(name2[1]) - parseFloat(name1[1])
    } else {
      return compare(name2[1], name1[1])
    }
  } else {
    return compare(name1[0], name2[0])
  }
})

return uniq(result)

}

function parse (queries) {

var qs = []
do {
  queries = doMatch(queries, qs)
} while (queries)
return qs

}

function doMatch (string, qs) {

var or = /^(?:,\s*|\s+OR\s+)(.*)/i
var and = /^\s+AND\s+(.*)/i

return find(string, function (parsed, n, max) {
  if (and.test(parsed)) {
    qs.unshift({ type: QUERY_AND, queryString: parsed.match(and)[1] })
    return true
  } else if (or.test(parsed)) {
    qs.unshift({ type: QUERY_OR, queryString: parsed.match(or)[1] })
    return true
  } else if (n === max) {
    qs.unshift({ type: QUERY_OR, queryString: parsed.trim() })
    return true
  }
  return false
})

}

function find (string, predicate) {

for (var n = 1, max = string.length; n <= max; n++) {
  var parsed = string.substr(-n, n)
  if (predicate(parsed, n, max)) {
    return string.slice(0, -n)
  }
}
return ''

}

function flatten (array) {

if (!Array.isArray(array)) return [array]
return array.reduce(function (a, b) {
  return a.concat(flatten(b))
}, [])

}

// Will be filled by Can I Use data below browserslist.data = { } browserslist.usage = {

global: { },
custom: null

}

// Default browsers query browserslist.defaults = [

'> 0.5%',
'last 2 versions',
'Firefox ESR',
'not dead'

]

// Browser names aliases browserslist.aliases = {

fx: 'firefox',
ff: 'firefox',
ios: 'ios_saf',
explorer: 'ie',
blackberry: 'bb',
explorermobile: 'ie_mob',
operamini: 'op_mini',
operamobile: 'op_mob',
chromeandroid: 'and_chr',
firefoxandroid: 'and_ff',
ucandroid: 'and_uc',
qqandroid: 'and_qq'

}

// Can I Use only provides a few versions for some browsers (e.g. and_chr). // Fallback to a similar browser for unknown versions browserslist.desktopNames = {

and_chr: 'chrome',
and_ff: 'firefox',
ie_mob: 'ie',
op_mob: 'opera'

}

// Aliases to work with joined versions like `ios_saf 7.0-7.1` browserslist.versionAliases = { }

browserslist.clearCaches = env.clearCaches browserslist.parseConfig = env.parseConfig browserslist.readConfig = env.readConfig browserslist.findConfig = env.findConfig browserslist.loadConfig = env.loadConfig

/**

* Return browsers market coverage.
*
* @param {string[]} browsers Browsers names in Can I Use.
* @param {string|object} [stats="global"] Which statistics should be used.
*                                         Country code or custom statistics.
*                                         Pass `"my stats"` to load statistics
*                                         from Browserslist files.
*
* @return {number} Total market coverage for all selected browsers.
*
* @example
* browserslist.coverage(browserslist('> 1% in US'), 'US') //=> 83.1
*/

browserslist.coverage = function (browsers, stats) {

var data
if (typeof stats === 'undefined') {
  data = browserslist.usage.global
} else if (stats === 'my stats') {
  var opts = {}
  opts.path = path.resolve ? path.resolve('.') : '.'
  var customStats = env.getStat(opts)
  if (!customStats) {
    throw new BrowserslistError('Custom usage statistics was not provided')
  }
  data = {}
  for (var browser in customStats) {
    fillUsage(data, browser, customStats[browser])
  }
} else if (typeof stats === 'string') {
  if (stats.length > 2) {
    stats = stats.toLowerCase()
  } else {
    stats = stats.toUpperCase()
  }
  env.loadCountry(browserslist.usage, stats)
  data = browserslist.usage[stats]
} else {
  if ('dataByBrowser' in stats) {
    stats = stats.dataByBrowser
  }
  data = { }
  for (var name in stats) {
    for (var version in stats[name]) {
      data[name + ' ' + version] = stats[name][version]
    }
  }
}

return browsers.reduce(function (all, i) {
  var usage = data[i]
  if (usage === undefined) {
    usage = data[i.replace(/ \S+$/, ' 0')]
  }
  return all + (usage || 0)
}, 0)

}

var QUERIES = [

{
  regexp: /^last\s+(\d+)\s+major\s+versions?$/i,
  select: function (context, versions) {
    return Object.keys(agents).reduce(function (selected, name) {
      var data = byName(name)
      if (!data) return selected
      var list = getMajorVersions(data.released, versions)
      list = list.map(nameMapper(data.name))
      if (data.name === 'android') list = filterAndroid(list, versions)
      return selected.concat(list)
    }, [])
  }
},
{
  regexp: /^last\s+(\d+)\s+versions?$/i,
  select: function (context, versions) {
    return Object.keys(agents).reduce(function (selected, name) {
      var data = byName(name)
      if (!data) return selected
      var list = data.released.slice(-versions)
      list = list.map(nameMapper(data.name))
      if (data.name === 'android') list = filterAndroid(list, versions)
      return selected.concat(list)
    }, [])
  }
},
{
  regexp: /^last\s+(\d+)\s+electron\s+major\s+versions?$/i,
  select: function (context, versions) {
    var validVersions = getMajorVersions(Object.keys(e2c).reverse(), versions)
    return validVersions.map(function (i) {
      return 'chrome ' + e2c[i]
    })
  }
},
{
  regexp: /^last\s+(\d+)\s+(\w+)\s+major\s+versions?$/i,
  select: function (context, versions, name) {
    var data = checkName(name)
    var validVersions = getMajorVersions(data.released, versions)
    var list = validVersions.map(nameMapper(data.name))
    if (data.name === 'android') list = filterAndroid(list, versions)
    return list
  }
},
{
  regexp: /^last\s+(\d+)\s+electron\s+versions?$/i,
  select: function (context, versions) {
    return Object.keys(e2c).reverse().slice(-versions).map(function (i) {
      return 'chrome ' + e2c[i]
    })
  }
},
{
  regexp: /^last\s+(\d+)\s+(\w+)\s+versions?$/i,
  select: function (context, versions, name) {
    var data = checkName(name)
    var list = data.released.slice(-versions).map(nameMapper(data.name))
    if (data.name === 'android') list = filterAndroid(list, versions)
    return list
  }
},
{
  regexp: /^unreleased\s+versions$/i,
  select: function () {
    return Object.keys(agents).reduce(function (selected, name) {
      var data = byName(name)
      if (!data) return selected
      var list = data.versions.filter(function (v) {
        return data.released.indexOf(v) === -1
      })
      list = list.map(nameMapper(data.name))
      return selected.concat(list)
    }, [])
  }
},
{
  regexp: /^unreleased\s+electron\s+versions?$/i,
  select: function () {
    return []
  }
},
{
  regexp: /^unreleased\s+(\w+)\s+versions?$/i,
  select: function (context, name) {
    var data = checkName(name)
    return data.versions.filter(function (v) {
      return data.released.indexOf(v) === -1
    }).map(nameMapper(data.name))
  }
},
{
  regexp: /^last\s+(\d*.?\d+)\s+years?$/i,
  select: function (context, years) {
    return filterByYear(Date.now() - YEAR * years)
  }
},
{
  regexp: /^since (\d+)(?:-(\d+))?(?:-(\d+))?$/i,
  select: function (context, year, month, date) {
    year = parseInt(year)
    month = parseInt(month || '01') - 1
    date = parseInt(date || '01')
    return filterByYear(Date.UTC(year, month, date, 0, 0, 0))
  }
},
{
  regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%$/,
  select: function (context, sign, popularity) {
    popularity = parseFloat(popularity)
    var usage = browserslist.usage.global
    return Object.keys(usage).reduce(function (result, version) {
      if (sign === '>') {
        if (usage[version] > popularity) {
          result.push(version)
        }
      } else if (sign === '<') {
        if (usage[version] < popularity) {
          result.push(version)
        }
      } else if (sign === '<=') {
        if (usage[version] <= popularity) {
          result.push(version)
        }
      } else if (usage[version] >= popularity) {
        result.push(version)
      }
      return result
    }, [])
  }
},
{
  regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+my\s+stats$/,
  select: function (context, sign, popularity) {
    popularity = parseFloat(popularity)
    if (!context.customUsage) {
      throw new BrowserslistError('Custom usage statistics was not provided')
    }
    var usage = context.customUsage
    return Object.keys(usage).reduce(function (result, version) {
      if (sign === '>') {
        if (usage[version] > popularity) {
          result.push(version)
        }
      } else if (sign === '<') {
        if (usage[version] < popularity) {
          result.push(version)
        }
      } else if (sign === '<=') {
        if (usage[version] <= popularity) {
          result.push(version)
        }
      } else if (usage[version] >= popularity) {
        result.push(version)
      }
      return result
    }, [])
  }
},
{
  regexp: /^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+((alt-)?\w\w)$/,
  select: function (context, sign, popularity, place) {
    popularity = parseFloat(popularity)
    if (place.length === 2) {
      place = place.toUpperCase()
    } else {
      place = place.toLowerCase()
    }
    env.loadCountry(browserslist.usage, place)
    var usage = browserslist.usage[place]
    return Object.keys(usage).reduce(function (result, version) {
      if (sign === '>') {
        if (usage[version] > popularity) {
          result.push(version)
        }
      } else if (sign === '<') {
        if (usage[version] < popularity) {
          result.push(version)
        }
      } else if (sign === '<=') {
        if (usage[version] <= popularity) {
          result.push(version)
        }
      } else if (usage[version] >= popularity) {
        result.push(version)
      }
      return result
    }, [])
  }
},
{
  regexp: /^cover\s+(\d*\.?\d+)%(\s+in\s+(my\s+stats|(alt-)?\w\w))?$/,
  select: function (context, coverage, statMode) {
    coverage = parseFloat(coverage)
    var usage = browserslist.usage.global
    if (statMode) {
      if (statMode.match(/^\s+in\s+my\s+stats$/)) {
        if (!context.customUsage) {
          throw new BrowserslistError(
            'Custom usage statistics was not provided'
          )
        }
        usage = context.customUsage
      } else {
        var match = statMode.match(/\s+in\s+((alt-)?\w\w)/)
        var place = match[1]
        if (place.length === 2) {
          place = place.toUpperCase()
        } else {
          place = place.toLowerCase()
        }
        env.loadCountry(browserslist.usage, place)
        usage = browserslist.usage[place]
      }
    }
    var versions = Object.keys(usage).sort(function (a, b) {
      return usage[b] - usage[a]
    })
    var coveraged = 0
    var result = []
    var version
    for (var i = 0; i <= versions.length; i++) {
      version = versions[i]
      if (usage[version] === 0) break
      coveraged += usage[version]
      result.push(version)
      if (coveraged >= coverage) break
    }
    return result
  }
},
{
  regexp: /^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  select: function (context, from, to) {
    var fromToUse = normalizeElectron(from)
    var toToUse = normalizeElectron(to)
    if (!e2c[fromToUse]) {
      throw new BrowserslistError('Unknown version ' + from + ' of electron')
    }
    if (!e2c[toToUse]) {
      throw new BrowserslistError('Unknown version ' + to + ' of electron')
    }
    from = parseFloat(from)
    to = parseFloat(to)
    return Object.keys(e2c).filter(function (i) {
      var parsed = parseFloat(i)
      return parsed >= from && parsed <= to
    }).map(function (i) {
      return 'chrome ' + e2c[i]
    })
  }
},
{
  regexp: /^(\w+)\s+([\d.]+)\s*-\s*([\d.]+)$/i,
  select: function (context, name, from, to) {
    var data = checkName(name)
    from = parseFloat(normalizeVersion(data, from, context) || from)
    to = parseFloat(normalizeVersion(data, to, context) || to)
    function filter (v) {
      var parsed = parseFloat(v)
      return parsed >= from && parsed <= to
    }
    return data.released.filter(filter).map(nameMapper(data.name))
  }
},
{
  regexp: /^electron\s*(>=?|<=?)\s*([\d.]+)$/i,
  select: function (context, sign, version) {
    var versionToUse = normalizeElectron(version)
    return Object.keys(e2c)
      .filter(generateFilter(sign, versionToUse))
      .map(function (i) {
        return 'chrome ' + e2c[i]
      })
  }
},
{
  regexp: /^node\s*(>=?|<=?)\s*([\d.]+)$/i,
  select: function (context, sign, version) {
    var nodeVersions = jsReleases.filter(function (i) {
      return i.name === 'nodejs'
    }).map(function (i) {
      return i.version
    })
    return nodeVersions
      .filter(generateSemverFilter(sign, version))
      .map(function (v) {
        return 'node ' + v
      })
  }
},
{
  regexp: /^(\w+)\s*(>=?|<=?)\s*([\d.]+)$/,
  select: function (context, name, sign, version) {
    var data = checkName(name)
    var alias = browserslist.versionAliases[data.name][version]
    if (alias) {
      version = alias
    }
    return data.released
      .filter(generateFilter(sign, version))
      .map(function (v) {
        return data.name + ' ' + v
      })
  }
},
{
  regexp: /^(firefox|ff|fx)\s+esr$/i,
  select: function () {
    return ['firefox 68', 'firefox 60']
  }
},
{
  regexp: /(operamini|op_mini)\s+all/i,
  select: function () {
    return ['op_mini all']
  }
},
{
  regexp: /^electron\s+([\d.]+)$/i,
  select: function (context, version) {
    var versionToUse = normalizeElectron(version)
    var chrome = e2c[versionToUse]
    if (!chrome) {
      throw new BrowserslistError(
        'Unknown version ' + version + ' of electron')
    }
    return ['chrome ' + chrome]
  }
},
{
  regexp: /^node\s+(\d+(\.\d+)?(\.\d+)?)$/i,
  select: function (context, version) {
    var nodeReleases = jsReleases.filter(function (i) {
      return i.name === 'nodejs'
    })
    var matched = nodeReleases.filter(function (i) {
      return isVersionsMatch(i.version, version)
    })
    if (matched.length === 0) {
      if (context.ignoreUnknownVersions) {
        return []
      } else {
        throw new BrowserslistError(
          'Unknown version ' + version + ' of Node.js')
      }
    }
    return ['node ' + matched[matched.length - 1].version]
  }
},
{
  regexp: /^current\s+node$/i,
  select: function (context) {
    return [env.currentNode(resolve, context)]
  }
},
{
  regexp: /^maintained\s+node\s+versions$/i,
  select: function (context) {
    var now = Date.now()
    var queries = Object.keys(jsEOL).filter(function (key) {
      return now < Date.parse(jsEOL[key].end) &&
        now > Date.parse(jsEOL[key].start) &&
        isEolReleased(key)
    }).map(function (key) {
      return 'node ' + key.slice(1)
    })
    return resolve(queries, context)
  }
},
{
  regexp: /^phantomjs\s+1.9$/i,
  select: function () {
    return ['safari 5']
  }
},
{
  regexp: /^phantomjs\s+2.1$/i,
  select: function () {
    return ['safari 6']
  }
},
{
  regexp: /^(\w+)\s+(tp|[\d.]+)$/i,
  select: function (context, name, version) {
    if (/^tp$/i.test(version)) version = 'TP'
    var data = checkName(name)
    var alias = normalizeVersion(data, version, context)
    if (alias) {
      version = alias
    } else {
      if (version.indexOf('.') === -1) {
        alias = version + '.0'
      } else {
        alias = version.replace(/\.0$/, '')
      }
      alias = normalizeVersion(data, alias, context)
      if (alias) {
        version = alias
      } else if (context.ignoreUnknownVersions) {
        return []
      } else {
        throw new BrowserslistError(
          'Unknown version ' + version + ' of ' + name)
      }
    }
    return [data.name + ' ' + version]
  }
},
{
  regexp: /^extends (.+)$/i,
  select: function (context, name) {
    return resolve(env.loadQueries(context, name), context)
  }
},
{
  regexp: /^defaults$/i,
  select: function () {
    return browserslist(browserslist.defaults)
  }
},
{
  regexp: /^dead$/i,
  select: function (context) {
    var dead = [
      'ie <= 10', 'ie_mob <= 10',
      'bb <= 10',
      'op_mob <= 12.1',
      'samsung 4'
    ]
    return resolve(dead, context)
  }
},
{
  regexp: /^(\w+)$/i,
  select: function (context, name) {
    if (byName(name)) {
      throw new BrowserslistError(
        'Specify versions in Browserslist query for browser ' + name)
    } else {
      throw unknownQuery(name)
    }
  }
}

];

// Get and convert Can I Use data

(function () {

for (var name in agents) {
  var browser = agents[name]
  browserslist.data[name] = {
    name: name,
    versions: normalize(agents[name].versions),
    released: normalize(agents[name].versions.slice(0, -3)),
    releaseDate: agents[name].release_date
  }
  fillUsage(browserslist.usage.global, name, browser.usage_global)

  browserslist.versionAliases[name] = { }
  for (var i = 0; i < browser.versions.length; i++) {
    var full = browser.versions[i]
    if (!full) continue

    if (full.indexOf('-') !== -1) {
      var interval = full.split('-')
      for (var j = 0; j < interval.length; j++) {
        browserslist.versionAliases[name][interval[j]] = full
      }
    }
  }
}

}())

module.exports = browserslist