Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

npa.js 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. 'use strict'
  2. module.exports = npa
  3. module.exports.resolve = resolve
  4. module.exports.Result = Result
  5. let url
  6. let HostedGit
  7. let semver
  8. let path_
  9. function path () {
  10. if (!path_) path_ = require('path')
  11. return path_
  12. }
  13. let validatePackageName
  14. let os
  15. const isWindows = process.platform === 'win32' || global.FAKE_WINDOWS
  16. const hasSlashes = isWindows ? /\\|[/]/ : /[/]/
  17. const isURL = /^(?:git[+])?[a-z]+:/i
  18. const isFilename = /[.](?:tgz|tar.gz|tar)$/i
  19. function npa (arg, where) {
  20. let name
  21. let spec
  22. if (typeof arg === 'object') {
  23. if (arg instanceof Result && (!where || where === arg.where)) {
  24. return arg
  25. } else if (arg.name && arg.rawSpec) {
  26. return npa.resolve(arg.name, arg.rawSpec, where || arg.where)
  27. } else {
  28. return npa(arg.raw, where || arg.where)
  29. }
  30. }
  31. const nameEndsAt = arg[0] === '@' ? arg.slice(1).indexOf('@') + 1 : arg.indexOf('@')
  32. const namePart = nameEndsAt > 0 ? arg.slice(0, nameEndsAt) : arg
  33. if (isURL.test(arg)) {
  34. spec = arg
  35. } else if (namePart[0] !== '@' && (hasSlashes.test(namePart) || isFilename.test(namePart))) {
  36. spec = arg
  37. } else if (nameEndsAt > 0) {
  38. name = namePart
  39. spec = arg.slice(nameEndsAt + 1)
  40. } else {
  41. if (!validatePackageName) validatePackageName = require('validate-npm-package-name')
  42. const valid = validatePackageName(arg)
  43. if (valid.validForOldPackages) {
  44. name = arg
  45. } else {
  46. spec = arg
  47. }
  48. }
  49. return resolve(name, spec, where, arg)
  50. }
  51. const isFilespec = isWindows ? /^(?:[.]|~[/]|[/\\]|[a-zA-Z]:)/ : /^(?:[.]|~[/]|[/]|[a-zA-Z]:)/
  52. function resolve (name, spec, where, arg) {
  53. const res = new Result({
  54. raw: arg,
  55. name: name,
  56. rawSpec: spec,
  57. fromArgument: arg != null
  58. })
  59. if (name) res.setName(name)
  60. if (spec && (isFilespec.test(spec) || /^file:/i.test(spec))) {
  61. return fromFile(res, where)
  62. } else if (spec && /^npm:/i.test(spec)) {
  63. return fromAlias(res, where)
  64. }
  65. if (!HostedGit) HostedGit = require('hosted-git-info')
  66. const hosted = HostedGit.fromUrl(spec, { noGitPlus: true, noCommittish: true })
  67. if (hosted) {
  68. return fromHostedGit(res, hosted)
  69. } else if (spec && isURL.test(spec)) {
  70. return fromURL(res)
  71. } else if (spec && (hasSlashes.test(spec) || isFilename.test(spec))) {
  72. return fromFile(res, where)
  73. } else {
  74. return fromRegistry(res)
  75. }
  76. }
  77. function invalidPackageName (name, valid) {
  78. const err = new Error(`Invalid package name "${name}": ${valid.errors.join('; ')}`)
  79. err.code = 'EINVALIDPACKAGENAME'
  80. return err
  81. }
  82. function invalidTagName (name) {
  83. const err = new Error(`Invalid tag name "${name}": Tags may not have any characters that encodeURIComponent encodes.`)
  84. err.code = 'EINVALIDTAGNAME'
  85. return err
  86. }
  87. function Result (opts) {
  88. this.type = opts.type
  89. this.registry = opts.registry
  90. this.where = opts.where
  91. if (opts.raw == null) {
  92. this.raw = opts.name ? opts.name + '@' + opts.rawSpec : opts.rawSpec
  93. } else {
  94. this.raw = opts.raw
  95. }
  96. this.name = undefined
  97. this.escapedName = undefined
  98. this.scope = undefined
  99. this.rawSpec = opts.rawSpec == null ? '' : opts.rawSpec
  100. this.saveSpec = opts.saveSpec
  101. this.fetchSpec = opts.fetchSpec
  102. if (opts.name) this.setName(opts.name)
  103. this.gitRange = opts.gitRange
  104. this.gitCommittish = opts.gitCommittish
  105. this.hosted = opts.hosted
  106. }
  107. Result.prototype.setName = function (name) {
  108. if (!validatePackageName) validatePackageName = require('validate-npm-package-name')
  109. const valid = validatePackageName(name)
  110. if (!valid.validForOldPackages) {
  111. throw invalidPackageName(name, valid)
  112. }
  113. this.name = name
  114. this.scope = name[0] === '@' ? name.slice(0, name.indexOf('/')) : undefined
  115. // scoped packages in couch must have slash url-encoded, e.g. @foo%2Fbar
  116. this.escapedName = name.replace('/', '%2f')
  117. return this
  118. }
  119. Result.prototype.toString = function () {
  120. const full = []
  121. if (this.name != null && this.name !== '') full.push(this.name)
  122. const spec = this.saveSpec || this.fetchSpec || this.rawSpec
  123. if (spec != null && spec !== '') full.push(spec)
  124. return full.length ? full.join('@') : this.raw
  125. }
  126. Result.prototype.toJSON = function () {
  127. const result = Object.assign({}, this)
  128. delete result.hosted
  129. return result
  130. }
  131. function setGitCommittish (res, committish) {
  132. if (committish != null && committish.length >= 7 && committish.slice(0, 7) === 'semver:') {
  133. res.gitRange = decodeURIComponent(committish.slice(7))
  134. res.gitCommittish = null
  135. } else {
  136. res.gitCommittish = committish === '' ? null : committish
  137. }
  138. return res
  139. }
  140. const isAbsolutePath = /^[/]|^[A-Za-z]:/
  141. function resolvePath (where, spec) {
  142. if (isAbsolutePath.test(spec)) return spec
  143. return path().resolve(where, spec)
  144. }
  145. function isAbsolute (dir) {
  146. if (dir[0] === '/') return true
  147. if (/^[A-Za-z]:/.test(dir)) return true
  148. return false
  149. }
  150. function fromFile (res, where) {
  151. if (!where) where = process.cwd()
  152. res.type = isFilename.test(res.rawSpec) ? 'file' : 'directory'
  153. res.where = where
  154. const spec = res.rawSpec.replace(/\\/g, '/')
  155. .replace(/^file:[/]*([A-Za-z]:)/, '$1') // drive name paths on windows
  156. .replace(/^file:(?:[/]*([~./]))?/, '$1')
  157. if (/^~[/]/.test(spec)) {
  158. // this is needed for windows and for file:~/foo/bar
  159. if (!os) os = require('os')
  160. res.fetchSpec = resolvePath(os.homedir(), spec.slice(2))
  161. res.saveSpec = 'file:' + spec
  162. } else {
  163. res.fetchSpec = resolvePath(where, spec)
  164. if (isAbsolute(spec)) {
  165. res.saveSpec = 'file:' + spec
  166. } else {
  167. res.saveSpec = 'file:' + path().relative(where, res.fetchSpec)
  168. }
  169. }
  170. return res
  171. }
  172. function fromHostedGit (res, hosted) {
  173. res.type = 'git'
  174. res.hosted = hosted
  175. res.saveSpec = hosted.toString({ noGitPlus: false, noCommittish: false })
  176. res.fetchSpec = hosted.getDefaultRepresentation() === 'shortcut' ? null : hosted.toString()
  177. return setGitCommittish(res, hosted.committish)
  178. }
  179. function unsupportedURLType (protocol, spec) {
  180. const err = new Error(`Unsupported URL Type "${protocol}": ${spec}`)
  181. err.code = 'EUNSUPPORTEDPROTOCOL'
  182. return err
  183. }
  184. function matchGitScp (spec) {
  185. // git ssh specifiers are overloaded to also use scp-style git
  186. // specifiers, so we have to parse those out and treat them special.
  187. // They are NOT true URIs, so we can't hand them to `url.parse`.
  188. //
  189. // This regex looks for things that look like:
  190. // git+ssh://git@my.custom.git.com:username/project.git#deadbeef
  191. //
  192. // ...and various combinations. The username in the beginning is *required*.
  193. const matched = spec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
  194. return matched && !matched[1].match(/:[0-9]+\/?.*$/i) && {
  195. fetchSpec: matched[1],
  196. gitCommittish: matched[2] == null ? null : matched[2]
  197. }
  198. }
  199. function fromURL (res) {
  200. if (!url) url = require('url')
  201. const urlparse = url.parse(res.rawSpec)
  202. res.saveSpec = res.rawSpec
  203. // check the protocol, and then see if it's git or not
  204. switch (urlparse.protocol) {
  205. case 'git:':
  206. case 'git+http:':
  207. case 'git+https:':
  208. case 'git+rsync:':
  209. case 'git+ftp:':
  210. case 'git+file:':
  211. case 'git+ssh:':
  212. res.type = 'git'
  213. const match = urlparse.protocol === 'git+ssh:' && matchGitScp(res.rawSpec)
  214. if (match) {
  215. setGitCommittish(res, match.gitCommittish)
  216. res.fetchSpec = match.fetchSpec
  217. } else {
  218. setGitCommittish(res, urlparse.hash != null ? urlparse.hash.slice(1) : '')
  219. urlparse.protocol = urlparse.protocol.replace(/^git[+]/, '')
  220. if (urlparse.protocol === 'file:' && /^git\+file:\/\/[a-z]:/i.test(res.rawSpec)) {
  221. // keep the drive letter : on windows file paths
  222. urlparse.host += ':'
  223. urlparse.hostname += ':'
  224. }
  225. delete urlparse.hash
  226. res.fetchSpec = url.format(urlparse)
  227. }
  228. break
  229. case 'http:':
  230. case 'https:':
  231. res.type = 'remote'
  232. res.fetchSpec = res.saveSpec
  233. break
  234. default:
  235. throw unsupportedURLType(urlparse.protocol, res.rawSpec)
  236. }
  237. return res
  238. }
  239. function fromAlias (res, where) {
  240. const subSpec = npa(res.rawSpec.substr(4), where)
  241. if (subSpec.type === 'alias') {
  242. throw new Error('nested aliases not supported')
  243. }
  244. if (!subSpec.registry) {
  245. throw new Error('aliases only work for registry deps')
  246. }
  247. res.subSpec = subSpec
  248. res.registry = true
  249. res.type = 'alias'
  250. res.saveSpec = null
  251. res.fetchSpec = null
  252. return res
  253. }
  254. function fromRegistry (res) {
  255. res.registry = true
  256. const spec = res.rawSpec === '' ? 'latest' : res.rawSpec
  257. // no save spec for registry components as we save based on the fetched
  258. // version, not on the argument so this can't compute that.
  259. res.saveSpec = null
  260. res.fetchSpec = spec
  261. if (!semver) semver = require('semver')
  262. const version = semver.valid(spec, true)
  263. const range = semver.validRange(spec, true)
  264. if (version) {
  265. res.type = 'version'
  266. } else if (range) {
  267. res.type = 'range'
  268. } else {
  269. if (encodeURIComponent(spec) !== spec) {
  270. throw invalidTagName(spec)
  271. }
  272. res.type = 'tag'
  273. }
  274. return res
  275. }