mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
4.5 KiB
187 lines
4.5 KiB
var url = require("url")
|
|
var assert = require("assert")
|
|
var util = require("util")
|
|
var semver = require("semver")
|
|
var path = require("path")
|
|
|
|
module.exports = npa
|
|
|
|
var isWindows = process.platform === "win32" || global.FAKE_WINDOWS
|
|
var slashRe = isWindows ? /\\|\// : /\//
|
|
|
|
var parseName = /^(?:@([^\/]+?)\/)?([^\/]+?)$/
|
|
var nameAt = /^(@([^\/]+?)\/)?([^\/]+?)@/
|
|
var debug = util.debuglog ? util.debuglog("npa")
|
|
: /\bnpa\b/i.test(process.env.NODE_DEBUG || "")
|
|
? function () {
|
|
console.error("NPA: " + util.format.apply(util, arguments).split("\n").join("\nNPA: "))
|
|
} : function () {}
|
|
|
|
function validName (name) {
|
|
if (!name) {
|
|
debug("not a name %j", name)
|
|
return false
|
|
}
|
|
var n = name.trim()
|
|
if (!n || n.charAt(0) === "."
|
|
|| !n.match(/^[a-zA-Z0-9]/)
|
|
|| n.match(/[\/\(\)&\?#\|<>@:%\s\\\*'"!~`]/)
|
|
|| n.toLowerCase() === "node_modules"
|
|
|| n !== encodeURIComponent(n)
|
|
|| n.toLowerCase() === "favicon.ico") {
|
|
debug("not a valid name %j", name)
|
|
return false
|
|
}
|
|
return n
|
|
}
|
|
|
|
function npa (arg) {
|
|
assert.equal(typeof arg, "string")
|
|
arg = arg.trim()
|
|
|
|
var res = new Result
|
|
res.raw = arg
|
|
res.scope = null
|
|
|
|
// See if it's something like foo@...
|
|
var nameparse = arg.match(nameAt)
|
|
debug("nameparse", nameparse)
|
|
if (nameparse && validName(nameparse[3]) &&
|
|
(!nameparse[2] || validName(nameparse[2]))) {
|
|
res.name = (nameparse[1] || "") + nameparse[3]
|
|
if (nameparse[2])
|
|
res.scope = "@" + nameparse[2]
|
|
arg = arg.substr(nameparse[0].length)
|
|
} else {
|
|
res.name = null
|
|
}
|
|
|
|
res.rawSpec = arg
|
|
res.spec = arg
|
|
|
|
var urlparse = url.parse(arg)
|
|
debug("urlparse", urlparse)
|
|
|
|
// windows paths look like urls
|
|
// don't be fooled!
|
|
if (isWindows && urlparse && urlparse.protocol &&
|
|
urlparse.protocol.match(/^[a-zA-Z]:$/)) {
|
|
debug("windows url-ish local path", urlparse)
|
|
urlparse = {}
|
|
}
|
|
|
|
if (urlparse.protocol) {
|
|
return parseUrl(res, arg, urlparse)
|
|
}
|
|
|
|
// parse git stuff
|
|
// parse tag/range/local/remote
|
|
|
|
if (maybeGitHubShorthand(arg)) {
|
|
res.type = "github"
|
|
res.spec = arg
|
|
return res
|
|
}
|
|
|
|
// at this point, it's not a url, and not github
|
|
// If it's a valid name, and doesn't already have a name, then assume
|
|
// $name@"" range
|
|
//
|
|
// if it's got / chars in it, then assume that it's local.
|
|
|
|
if (res.name) {
|
|
var version = semver.valid(arg, true)
|
|
var range = semver.validRange(arg, true)
|
|
// foo@...
|
|
if (version) {
|
|
res.spec = version
|
|
res.type = "version"
|
|
} else if (range) {
|
|
res.spec = range
|
|
res.type = "range"
|
|
} else if (slashRe.test(arg)) {
|
|
parseLocal(res, arg)
|
|
} else {
|
|
res.type = "tag"
|
|
res.spec = arg
|
|
}
|
|
} else {
|
|
var p = arg.match(parseName)
|
|
if (p && validName(p[2]) &&
|
|
(!p[1] || validName(p[1]))) {
|
|
res.type = "range"
|
|
res.spec = "*"
|
|
res.rawSpec = ""
|
|
res.name = arg
|
|
if (p[1])
|
|
res.scope = "@" + p[1]
|
|
} else {
|
|
parseLocal(res, arg)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
function parseLocal (res, arg) {
|
|
// turns out nearly every character is allowed in fs paths
|
|
if (/\0/.test(arg)) {
|
|
throw new Error("Invalid Path: " + JSON.stringify(arg))
|
|
}
|
|
res.type = "local"
|
|
res.spec = path.resolve(arg)
|
|
}
|
|
|
|
function maybeGitHubShorthand (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 parseUrl (res, arg, urlparse) {
|
|
// check the protocol, and then see if it's git or not
|
|
switch (urlparse.protocol) {
|
|
case "git:":
|
|
case "git+http:":
|
|
case "git+https:":
|
|
case "git+rsync:":
|
|
case "git+ftp:":
|
|
case "git+ssh:":
|
|
case "git+file:":
|
|
res.type = 'git'
|
|
res.spec = arg.replace(/^git\+/, '')
|
|
break
|
|
|
|
case 'http:':
|
|
case 'https:':
|
|
res.type = 'remote'
|
|
res.spec = arg
|
|
break
|
|
|
|
case 'file:':
|
|
res.type = 'local'
|
|
res.spec = urlparse.pathname
|
|
break;
|
|
|
|
default:
|
|
throw new Error('Unsupported URL Type: ' + arg)
|
|
break
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
|
|
function Result () {
|
|
if (!(this instanceof Result)) return new Result
|
|
}
|
|
Result.prototype.name = null
|
|
Result.prototype.type = null
|
|
Result.prototype.spec = null
|
|
Result.prototype.raw = null
|
|
|