11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_cimodule.exports = npa
31cb0ef41Sopenharmony_cimodule.exports.resolve = resolve
41cb0ef41Sopenharmony_cimodule.exports.toPurl = toPurl
51cb0ef41Sopenharmony_cimodule.exports.Result = Result
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst { URL } = require('url')
81cb0ef41Sopenharmony_ciconst HostedGit = require('hosted-git-info')
91cb0ef41Sopenharmony_ciconst semver = require('semver')
101cb0ef41Sopenharmony_ciconst path = global.FAKE_WINDOWS ? require('path').win32 : require('path')
111cb0ef41Sopenharmony_ciconst validatePackageName = require('validate-npm-package-name')
121cb0ef41Sopenharmony_ciconst { homedir } = require('os')
131cb0ef41Sopenharmony_ciconst log = require('proc-log')
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ciconst isWindows = process.platform === 'win32' || global.FAKE_WINDOWS
161cb0ef41Sopenharmony_ciconst hasSlashes = isWindows ? /\\|[/]/ : /[/]/
171cb0ef41Sopenharmony_ciconst isURL = /^(?:git[+])?[a-z]+:/i
181cb0ef41Sopenharmony_ciconst isGit = /^[^@]+@[^:.]+\.[^:]+:.+$/i
191cb0ef41Sopenharmony_ciconst isFilename = /[.](?:tgz|tar.gz|tar)$/i
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_cifunction npa (arg, where) {
221cb0ef41Sopenharmony_ci  let name
231cb0ef41Sopenharmony_ci  let spec
241cb0ef41Sopenharmony_ci  if (typeof arg === 'object') {
251cb0ef41Sopenharmony_ci    if (arg instanceof Result && (!where || where === arg.where)) {
261cb0ef41Sopenharmony_ci      return arg
271cb0ef41Sopenharmony_ci    } else if (arg.name && arg.rawSpec) {
281cb0ef41Sopenharmony_ci      return npa.resolve(arg.name, arg.rawSpec, where || arg.where)
291cb0ef41Sopenharmony_ci    } else {
301cb0ef41Sopenharmony_ci      return npa(arg.raw, where || arg.where)
311cb0ef41Sopenharmony_ci    }
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci  const nameEndsAt = arg[0] === '@' ? arg.slice(1).indexOf('@') + 1 : arg.indexOf('@')
341cb0ef41Sopenharmony_ci  const namePart = nameEndsAt > 0 ? arg.slice(0, nameEndsAt) : arg
351cb0ef41Sopenharmony_ci  if (isURL.test(arg)) {
361cb0ef41Sopenharmony_ci    spec = arg
371cb0ef41Sopenharmony_ci  } else if (isGit.test(arg)) {
381cb0ef41Sopenharmony_ci    spec = `git+ssh://${arg}`
391cb0ef41Sopenharmony_ci  } else if (namePart[0] !== '@' && (hasSlashes.test(namePart) || isFilename.test(namePart))) {
401cb0ef41Sopenharmony_ci    spec = arg
411cb0ef41Sopenharmony_ci  } else if (nameEndsAt > 0) {
421cb0ef41Sopenharmony_ci    name = namePart
431cb0ef41Sopenharmony_ci    spec = arg.slice(nameEndsAt + 1) || '*'
441cb0ef41Sopenharmony_ci  } else {
451cb0ef41Sopenharmony_ci    const valid = validatePackageName(arg)
461cb0ef41Sopenharmony_ci    if (valid.validForOldPackages) {
471cb0ef41Sopenharmony_ci      name = arg
481cb0ef41Sopenharmony_ci      spec = '*'
491cb0ef41Sopenharmony_ci    } else {
501cb0ef41Sopenharmony_ci      spec = arg
511cb0ef41Sopenharmony_ci    }
521cb0ef41Sopenharmony_ci  }
531cb0ef41Sopenharmony_ci  return resolve(name, spec, where, arg)
541cb0ef41Sopenharmony_ci}
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ciconst isFilespec = isWindows ? /^(?:[.]|~[/]|[/\\]|[a-zA-Z]:)/ : /^(?:[.]|~[/]|[/]|[a-zA-Z]:)/
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_cifunction resolve (name, spec, where, arg) {
591cb0ef41Sopenharmony_ci  const res = new Result({
601cb0ef41Sopenharmony_ci    raw: arg,
611cb0ef41Sopenharmony_ci    name: name,
621cb0ef41Sopenharmony_ci    rawSpec: spec,
631cb0ef41Sopenharmony_ci    fromArgument: arg != null,
641cb0ef41Sopenharmony_ci  })
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  if (name) {
671cb0ef41Sopenharmony_ci    res.setName(name)
681cb0ef41Sopenharmony_ci  }
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci  if (spec && (isFilespec.test(spec) || /^file:/i.test(spec))) {
711cb0ef41Sopenharmony_ci    return fromFile(res, where)
721cb0ef41Sopenharmony_ci  } else if (spec && /^npm:/i.test(spec)) {
731cb0ef41Sopenharmony_ci    return fromAlias(res, where)
741cb0ef41Sopenharmony_ci  }
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci  const hosted = HostedGit.fromUrl(spec, {
771cb0ef41Sopenharmony_ci    noGitPlus: true,
781cb0ef41Sopenharmony_ci    noCommittish: true,
791cb0ef41Sopenharmony_ci  })
801cb0ef41Sopenharmony_ci  if (hosted) {
811cb0ef41Sopenharmony_ci    return fromHostedGit(res, hosted)
821cb0ef41Sopenharmony_ci  } else if (spec && isURL.test(spec)) {
831cb0ef41Sopenharmony_ci    return fromURL(res)
841cb0ef41Sopenharmony_ci  } else if (spec && (hasSlashes.test(spec) || isFilename.test(spec))) {
851cb0ef41Sopenharmony_ci    return fromFile(res, where)
861cb0ef41Sopenharmony_ci  } else {
871cb0ef41Sopenharmony_ci    return fromRegistry(res)
881cb0ef41Sopenharmony_ci  }
891cb0ef41Sopenharmony_ci}
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ciconst defaultRegistry = 'https://registry.npmjs.org'
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_cifunction toPurl (arg, reg = defaultRegistry) {
941cb0ef41Sopenharmony_ci  const res = npa(arg)
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  if (res.type !== 'version') {
971cb0ef41Sopenharmony_ci    throw invalidPurlType(res.type, res.raw)
981cb0ef41Sopenharmony_ci  }
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  // URI-encode leading @ of scoped packages
1011cb0ef41Sopenharmony_ci  let purl = 'pkg:npm/' + res.name.replace(/^@/, '%40') + '@' + res.rawSpec
1021cb0ef41Sopenharmony_ci  if (reg !== defaultRegistry) {
1031cb0ef41Sopenharmony_ci    purl += '?repository_url=' + reg
1041cb0ef41Sopenharmony_ci  }
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci  return purl
1071cb0ef41Sopenharmony_ci}
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_cifunction invalidPackageName (name, valid, raw) {
1101cb0ef41Sopenharmony_ci  // eslint-disable-next-line max-len
1111cb0ef41Sopenharmony_ci  const err = new Error(`Invalid package name "${name}" of package "${raw}": ${valid.errors.join('; ')}.`)
1121cb0ef41Sopenharmony_ci  err.code = 'EINVALIDPACKAGENAME'
1131cb0ef41Sopenharmony_ci  return err
1141cb0ef41Sopenharmony_ci}
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_cifunction invalidTagName (name, raw) {
1171cb0ef41Sopenharmony_ci  // eslint-disable-next-line max-len
1181cb0ef41Sopenharmony_ci  const err = new Error(`Invalid tag name "${name}" of package "${raw}": Tags may not have any characters that encodeURIComponent encodes.`)
1191cb0ef41Sopenharmony_ci  err.code = 'EINVALIDTAGNAME'
1201cb0ef41Sopenharmony_ci  return err
1211cb0ef41Sopenharmony_ci}
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_cifunction invalidPurlType (type, raw) {
1241cb0ef41Sopenharmony_ci  // eslint-disable-next-line max-len
1251cb0ef41Sopenharmony_ci  const err = new Error(`Invalid type "${type}" of package "${raw}": Purl can only be generated for "version" types.`)
1261cb0ef41Sopenharmony_ci  err.code = 'EINVALIDPURLTYPE'
1271cb0ef41Sopenharmony_ci  return err
1281cb0ef41Sopenharmony_ci}
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_cifunction Result (opts) {
1311cb0ef41Sopenharmony_ci  this.type = opts.type
1321cb0ef41Sopenharmony_ci  this.registry = opts.registry
1331cb0ef41Sopenharmony_ci  this.where = opts.where
1341cb0ef41Sopenharmony_ci  if (opts.raw == null) {
1351cb0ef41Sopenharmony_ci    this.raw = opts.name ? opts.name + '@' + opts.rawSpec : opts.rawSpec
1361cb0ef41Sopenharmony_ci  } else {
1371cb0ef41Sopenharmony_ci    this.raw = opts.raw
1381cb0ef41Sopenharmony_ci  }
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci  this.name = undefined
1411cb0ef41Sopenharmony_ci  this.escapedName = undefined
1421cb0ef41Sopenharmony_ci  this.scope = undefined
1431cb0ef41Sopenharmony_ci  this.rawSpec = opts.rawSpec || ''
1441cb0ef41Sopenharmony_ci  this.saveSpec = opts.saveSpec
1451cb0ef41Sopenharmony_ci  this.fetchSpec = opts.fetchSpec
1461cb0ef41Sopenharmony_ci  if (opts.name) {
1471cb0ef41Sopenharmony_ci    this.setName(opts.name)
1481cb0ef41Sopenharmony_ci  }
1491cb0ef41Sopenharmony_ci  this.gitRange = opts.gitRange
1501cb0ef41Sopenharmony_ci  this.gitCommittish = opts.gitCommittish
1511cb0ef41Sopenharmony_ci  this.gitSubdir = opts.gitSubdir
1521cb0ef41Sopenharmony_ci  this.hosted = opts.hosted
1531cb0ef41Sopenharmony_ci}
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ciResult.prototype.setName = function (name) {
1561cb0ef41Sopenharmony_ci  const valid = validatePackageName(name)
1571cb0ef41Sopenharmony_ci  if (!valid.validForOldPackages) {
1581cb0ef41Sopenharmony_ci    throw invalidPackageName(name, valid, this.raw)
1591cb0ef41Sopenharmony_ci  }
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  this.name = name
1621cb0ef41Sopenharmony_ci  this.scope = name[0] === '@' ? name.slice(0, name.indexOf('/')) : undefined
1631cb0ef41Sopenharmony_ci  // scoped packages in couch must have slash url-encoded, e.g. @foo%2Fbar
1641cb0ef41Sopenharmony_ci  this.escapedName = name.replace('/', '%2f')
1651cb0ef41Sopenharmony_ci  return this
1661cb0ef41Sopenharmony_ci}
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ciResult.prototype.toString = function () {
1691cb0ef41Sopenharmony_ci  const full = []
1701cb0ef41Sopenharmony_ci  if (this.name != null && this.name !== '') {
1711cb0ef41Sopenharmony_ci    full.push(this.name)
1721cb0ef41Sopenharmony_ci  }
1731cb0ef41Sopenharmony_ci  const spec = this.saveSpec || this.fetchSpec || this.rawSpec
1741cb0ef41Sopenharmony_ci  if (spec != null && spec !== '') {
1751cb0ef41Sopenharmony_ci    full.push(spec)
1761cb0ef41Sopenharmony_ci  }
1771cb0ef41Sopenharmony_ci  return full.length ? full.join('@') : this.raw
1781cb0ef41Sopenharmony_ci}
1791cb0ef41Sopenharmony_ci
1801cb0ef41Sopenharmony_ciResult.prototype.toJSON = function () {
1811cb0ef41Sopenharmony_ci  const result = Object.assign({}, this)
1821cb0ef41Sopenharmony_ci  delete result.hosted
1831cb0ef41Sopenharmony_ci  return result
1841cb0ef41Sopenharmony_ci}
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci// sets res.gitCommittish, res.gitRange, and res.gitSubdir
1871cb0ef41Sopenharmony_cifunction setGitAttrs (res, committish) {
1881cb0ef41Sopenharmony_ci  if (!committish) {
1891cb0ef41Sopenharmony_ci    res.gitCommittish = null
1901cb0ef41Sopenharmony_ci    return
1911cb0ef41Sopenharmony_ci  }
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci  // for each :: separated item:
1941cb0ef41Sopenharmony_ci  for (const part of committish.split('::')) {
1951cb0ef41Sopenharmony_ci    // if the item has no : the n it is a commit-ish
1961cb0ef41Sopenharmony_ci    if (!part.includes(':')) {
1971cb0ef41Sopenharmony_ci      if (res.gitRange) {
1981cb0ef41Sopenharmony_ci        throw new Error('cannot override existing semver range with a committish')
1991cb0ef41Sopenharmony_ci      }
2001cb0ef41Sopenharmony_ci      if (res.gitCommittish) {
2011cb0ef41Sopenharmony_ci        throw new Error('cannot override existing committish with a second committish')
2021cb0ef41Sopenharmony_ci      }
2031cb0ef41Sopenharmony_ci      res.gitCommittish = part
2041cb0ef41Sopenharmony_ci      continue
2051cb0ef41Sopenharmony_ci    }
2061cb0ef41Sopenharmony_ci    // split on name:value
2071cb0ef41Sopenharmony_ci    const [name, value] = part.split(':')
2081cb0ef41Sopenharmony_ci    // if name is semver do semver lookup of ref or tag
2091cb0ef41Sopenharmony_ci    if (name === 'semver') {
2101cb0ef41Sopenharmony_ci      if (res.gitCommittish) {
2111cb0ef41Sopenharmony_ci        throw new Error('cannot override existing committish with a semver range')
2121cb0ef41Sopenharmony_ci      }
2131cb0ef41Sopenharmony_ci      if (res.gitRange) {
2141cb0ef41Sopenharmony_ci        throw new Error('cannot override existing semver range with a second semver range')
2151cb0ef41Sopenharmony_ci      }
2161cb0ef41Sopenharmony_ci      res.gitRange = decodeURIComponent(value)
2171cb0ef41Sopenharmony_ci      continue
2181cb0ef41Sopenharmony_ci    }
2191cb0ef41Sopenharmony_ci    if (name === 'path') {
2201cb0ef41Sopenharmony_ci      if (res.gitSubdir) {
2211cb0ef41Sopenharmony_ci        throw new Error('cannot override existing path with a second path')
2221cb0ef41Sopenharmony_ci      }
2231cb0ef41Sopenharmony_ci      res.gitSubdir = `/${value}`
2241cb0ef41Sopenharmony_ci      continue
2251cb0ef41Sopenharmony_ci    }
2261cb0ef41Sopenharmony_ci    log.warn('npm-package-arg', `ignoring unknown key "${name}"`)
2271cb0ef41Sopenharmony_ci  }
2281cb0ef41Sopenharmony_ci}
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_cifunction fromFile (res, where) {
2311cb0ef41Sopenharmony_ci  if (!where) {
2321cb0ef41Sopenharmony_ci    where = process.cwd()
2331cb0ef41Sopenharmony_ci  }
2341cb0ef41Sopenharmony_ci  res.type = isFilename.test(res.rawSpec) ? 'file' : 'directory'
2351cb0ef41Sopenharmony_ci  res.where = where
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci  // always put the '/' on where when resolving urls, or else
2381cb0ef41Sopenharmony_ci  // file:foo from /path/to/bar goes to /path/to/foo, when we want
2391cb0ef41Sopenharmony_ci  // it to be /path/to/bar/foo
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci  let specUrl
2421cb0ef41Sopenharmony_ci  let resolvedUrl
2431cb0ef41Sopenharmony_ci  const prefix = (!/^file:/.test(res.rawSpec) ? 'file:' : '')
2441cb0ef41Sopenharmony_ci  const rawWithPrefix = prefix + res.rawSpec
2451cb0ef41Sopenharmony_ci  let rawNoPrefix = rawWithPrefix.replace(/^file:/, '')
2461cb0ef41Sopenharmony_ci  try {
2471cb0ef41Sopenharmony_ci    resolvedUrl = new URL(rawWithPrefix, `file://${path.resolve(where)}/`)
2481cb0ef41Sopenharmony_ci    specUrl = new URL(rawWithPrefix)
2491cb0ef41Sopenharmony_ci  } catch (originalError) {
2501cb0ef41Sopenharmony_ci    const er = new Error('Invalid file: URL, must comply with RFC 8089')
2511cb0ef41Sopenharmony_ci    throw Object.assign(er, {
2521cb0ef41Sopenharmony_ci      raw: res.rawSpec,
2531cb0ef41Sopenharmony_ci      spec: res,
2541cb0ef41Sopenharmony_ci      where,
2551cb0ef41Sopenharmony_ci      originalError,
2561cb0ef41Sopenharmony_ci    })
2571cb0ef41Sopenharmony_ci  }
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci  // XXX backwards compatibility lack of compliance with RFC 8089
2601cb0ef41Sopenharmony_ci  if (resolvedUrl.host && resolvedUrl.host !== 'localhost') {
2611cb0ef41Sopenharmony_ci    const rawSpec = res.rawSpec.replace(/^file:\/\//, 'file:///')
2621cb0ef41Sopenharmony_ci    resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
2631cb0ef41Sopenharmony_ci    specUrl = new URL(rawSpec)
2641cb0ef41Sopenharmony_ci    rawNoPrefix = rawSpec.replace(/^file:/, '')
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci  // turn file:/../foo into file:../foo
2671cb0ef41Sopenharmony_ci  // for 1, 2 or 3 leading slashes since we attempted
2681cb0ef41Sopenharmony_ci  // in the previous step to make it a file protocol url with a leading slash
2691cb0ef41Sopenharmony_ci  if (/^\/{1,3}\.\.?(\/|$)/.test(rawNoPrefix)) {
2701cb0ef41Sopenharmony_ci    const rawSpec = res.rawSpec.replace(/^file:\/{1,3}/, 'file:')
2711cb0ef41Sopenharmony_ci    resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
2721cb0ef41Sopenharmony_ci    specUrl = new URL(rawSpec)
2731cb0ef41Sopenharmony_ci    rawNoPrefix = rawSpec.replace(/^file:/, '')
2741cb0ef41Sopenharmony_ci  }
2751cb0ef41Sopenharmony_ci  // XXX end RFC 8089 violation backwards compatibility section
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ci  // turn /C:/blah into just C:/blah on windows
2781cb0ef41Sopenharmony_ci  let specPath = decodeURIComponent(specUrl.pathname)
2791cb0ef41Sopenharmony_ci  let resolvedPath = decodeURIComponent(resolvedUrl.pathname)
2801cb0ef41Sopenharmony_ci  if (isWindows) {
2811cb0ef41Sopenharmony_ci    specPath = specPath.replace(/^\/+([a-z]:\/)/i, '$1')
2821cb0ef41Sopenharmony_ci    resolvedPath = resolvedPath.replace(/^\/+([a-z]:\/)/i, '$1')
2831cb0ef41Sopenharmony_ci  }
2841cb0ef41Sopenharmony_ci
2851cb0ef41Sopenharmony_ci  // replace ~ with homedir, but keep the ~ in the saveSpec
2861cb0ef41Sopenharmony_ci  // otherwise, make it relative to where param
2871cb0ef41Sopenharmony_ci  if (/^\/~(\/|$)/.test(specPath)) {
2881cb0ef41Sopenharmony_ci    res.saveSpec = `file:${specPath.substr(1)}`
2891cb0ef41Sopenharmony_ci    resolvedPath = path.resolve(homedir(), specPath.substr(3))
2901cb0ef41Sopenharmony_ci  } else if (!path.isAbsolute(rawNoPrefix)) {
2911cb0ef41Sopenharmony_ci    res.saveSpec = `file:${path.relative(where, resolvedPath)}`
2921cb0ef41Sopenharmony_ci  } else {
2931cb0ef41Sopenharmony_ci    res.saveSpec = `file:${path.resolve(resolvedPath)}`
2941cb0ef41Sopenharmony_ci  }
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci  res.fetchSpec = path.resolve(where, resolvedPath)
2971cb0ef41Sopenharmony_ci  return res
2981cb0ef41Sopenharmony_ci}
2991cb0ef41Sopenharmony_ci
3001cb0ef41Sopenharmony_cifunction fromHostedGit (res, hosted) {
3011cb0ef41Sopenharmony_ci  res.type = 'git'
3021cb0ef41Sopenharmony_ci  res.hosted = hosted
3031cb0ef41Sopenharmony_ci  res.saveSpec = hosted.toString({ noGitPlus: false, noCommittish: false })
3041cb0ef41Sopenharmony_ci  res.fetchSpec = hosted.getDefaultRepresentation() === 'shortcut' ? null : hosted.toString()
3051cb0ef41Sopenharmony_ci  setGitAttrs(res, hosted.committish)
3061cb0ef41Sopenharmony_ci  return res
3071cb0ef41Sopenharmony_ci}
3081cb0ef41Sopenharmony_ci
3091cb0ef41Sopenharmony_cifunction unsupportedURLType (protocol, spec) {
3101cb0ef41Sopenharmony_ci  const err = new Error(`Unsupported URL Type "${protocol}": ${spec}`)
3111cb0ef41Sopenharmony_ci  err.code = 'EUNSUPPORTEDPROTOCOL'
3121cb0ef41Sopenharmony_ci  return err
3131cb0ef41Sopenharmony_ci}
3141cb0ef41Sopenharmony_ci
3151cb0ef41Sopenharmony_cifunction fromURL (res) {
3161cb0ef41Sopenharmony_ci  let rawSpec = res.rawSpec
3171cb0ef41Sopenharmony_ci  res.saveSpec = rawSpec
3181cb0ef41Sopenharmony_ci  if (rawSpec.startsWith('git+ssh:')) {
3191cb0ef41Sopenharmony_ci    // git ssh specifiers are overloaded to also use scp-style git
3201cb0ef41Sopenharmony_ci    // specifiers, so we have to parse those out and treat them special.
3211cb0ef41Sopenharmony_ci    // They are NOT true URIs, so we can't hand them to URL.
3221cb0ef41Sopenharmony_ci
3231cb0ef41Sopenharmony_ci    // This regex looks for things that look like:
3241cb0ef41Sopenharmony_ci    // git+ssh://git@my.custom.git.com:username/project.git#deadbeef
3251cb0ef41Sopenharmony_ci    // ...and various combinations. The username in the beginning is *required*.
3261cb0ef41Sopenharmony_ci    const matched = rawSpec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
3271cb0ef41Sopenharmony_ci    if (matched && !matched[1].match(/:[0-9]+\/?.*$/i)) {
3281cb0ef41Sopenharmony_ci      res.type = 'git'
3291cb0ef41Sopenharmony_ci      setGitAttrs(res, matched[2])
3301cb0ef41Sopenharmony_ci      res.fetchSpec = matched[1]
3311cb0ef41Sopenharmony_ci      return res
3321cb0ef41Sopenharmony_ci    }
3331cb0ef41Sopenharmony_ci  } else if (rawSpec.startsWith('git+file://')) {
3341cb0ef41Sopenharmony_ci    // URL can't handle windows paths
3351cb0ef41Sopenharmony_ci    rawSpec = rawSpec.replace(/\\/g, '/')
3361cb0ef41Sopenharmony_ci  }
3371cb0ef41Sopenharmony_ci  const parsedUrl = new URL(rawSpec)
3381cb0ef41Sopenharmony_ci  // check the protocol, and then see if it's git or not
3391cb0ef41Sopenharmony_ci  switch (parsedUrl.protocol) {
3401cb0ef41Sopenharmony_ci    case 'git:':
3411cb0ef41Sopenharmony_ci    case 'git+http:':
3421cb0ef41Sopenharmony_ci    case 'git+https:':
3431cb0ef41Sopenharmony_ci    case 'git+rsync:':
3441cb0ef41Sopenharmony_ci    case 'git+ftp:':
3451cb0ef41Sopenharmony_ci    case 'git+file:':
3461cb0ef41Sopenharmony_ci    case 'git+ssh:':
3471cb0ef41Sopenharmony_ci      res.type = 'git'
3481cb0ef41Sopenharmony_ci      setGitAttrs(res, parsedUrl.hash.slice(1))
3491cb0ef41Sopenharmony_ci      if (parsedUrl.protocol === 'git+file:' && /^git\+file:\/\/[a-z]:/i.test(rawSpec)) {
3501cb0ef41Sopenharmony_ci        // URL can't handle drive letters on windows file paths, the host can't contain a :
3511cb0ef41Sopenharmony_ci        res.fetchSpec = `git+file://${parsedUrl.host.toLowerCase()}:${parsedUrl.pathname}`
3521cb0ef41Sopenharmony_ci      } else {
3531cb0ef41Sopenharmony_ci        parsedUrl.hash = ''
3541cb0ef41Sopenharmony_ci        res.fetchSpec = parsedUrl.toString()
3551cb0ef41Sopenharmony_ci      }
3561cb0ef41Sopenharmony_ci      if (res.fetchSpec.startsWith('git+')) {
3571cb0ef41Sopenharmony_ci        res.fetchSpec = res.fetchSpec.slice(4)
3581cb0ef41Sopenharmony_ci      }
3591cb0ef41Sopenharmony_ci      break
3601cb0ef41Sopenharmony_ci    case 'http:':
3611cb0ef41Sopenharmony_ci    case 'https:':
3621cb0ef41Sopenharmony_ci      res.type = 'remote'
3631cb0ef41Sopenharmony_ci      res.fetchSpec = res.saveSpec
3641cb0ef41Sopenharmony_ci      break
3651cb0ef41Sopenharmony_ci
3661cb0ef41Sopenharmony_ci    default:
3671cb0ef41Sopenharmony_ci      throw unsupportedURLType(parsedUrl.protocol, rawSpec)
3681cb0ef41Sopenharmony_ci  }
3691cb0ef41Sopenharmony_ci
3701cb0ef41Sopenharmony_ci  return res
3711cb0ef41Sopenharmony_ci}
3721cb0ef41Sopenharmony_ci
3731cb0ef41Sopenharmony_cifunction fromAlias (res, where) {
3741cb0ef41Sopenharmony_ci  const subSpec = npa(res.rawSpec.substr(4), where)
3751cb0ef41Sopenharmony_ci  if (subSpec.type === 'alias') {
3761cb0ef41Sopenharmony_ci    throw new Error('nested aliases not supported')
3771cb0ef41Sopenharmony_ci  }
3781cb0ef41Sopenharmony_ci
3791cb0ef41Sopenharmony_ci  if (!subSpec.registry) {
3801cb0ef41Sopenharmony_ci    throw new Error('aliases only work for registry deps')
3811cb0ef41Sopenharmony_ci  }
3821cb0ef41Sopenharmony_ci
3831cb0ef41Sopenharmony_ci  res.subSpec = subSpec
3841cb0ef41Sopenharmony_ci  res.registry = true
3851cb0ef41Sopenharmony_ci  res.type = 'alias'
3861cb0ef41Sopenharmony_ci  res.saveSpec = null
3871cb0ef41Sopenharmony_ci  res.fetchSpec = null
3881cb0ef41Sopenharmony_ci  return res
3891cb0ef41Sopenharmony_ci}
3901cb0ef41Sopenharmony_ci
3911cb0ef41Sopenharmony_cifunction fromRegistry (res) {
3921cb0ef41Sopenharmony_ci  res.registry = true
3931cb0ef41Sopenharmony_ci  const spec = res.rawSpec.trim()
3941cb0ef41Sopenharmony_ci  // no save spec for registry components as we save based on the fetched
3951cb0ef41Sopenharmony_ci  // version, not on the argument so this can't compute that.
3961cb0ef41Sopenharmony_ci  res.saveSpec = null
3971cb0ef41Sopenharmony_ci  res.fetchSpec = spec
3981cb0ef41Sopenharmony_ci  const version = semver.valid(spec, true)
3991cb0ef41Sopenharmony_ci  const range = semver.validRange(spec, true)
4001cb0ef41Sopenharmony_ci  if (version) {
4011cb0ef41Sopenharmony_ci    res.type = 'version'
4021cb0ef41Sopenharmony_ci  } else if (range) {
4031cb0ef41Sopenharmony_ci    res.type = 'range'
4041cb0ef41Sopenharmony_ci  } else {
4051cb0ef41Sopenharmony_ci    if (encodeURIComponent(spec) !== spec) {
4061cb0ef41Sopenharmony_ci      throw invalidTagName(spec, res.raw)
4071cb0ef41Sopenharmony_ci    }
4081cb0ef41Sopenharmony_ci    res.type = 'tag'
4091cb0ef41Sopenharmony_ci  }
4101cb0ef41Sopenharmony_ci  return res
4111cb0ef41Sopenharmony_ci}
412