Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ var parsed = npa("@bar/foo@1.2")

// Returns an object like:
{
raw: '@bar/foo@1.2', // what was passed in
name: "@bar/foo", // the name of the package
scope: "@bar", // the private scope of the package, or null
type: "range", // the type of specifier this is
spec: ">=1.2.0 <1.3.0" // the expanded specifier
rawSpec: "1.2" // the specifier as passed in
raw: '@bar/foo@1.2', // what was passed in
name: '@bar/foo', // the name of the package
escapedName: '@bar%2ffoo', // the escaped name, for registry internals
scope: '@bar', // the scope of the package, or null
type: 'range', // the type of specifier this is
spec: '>=1.2.0 <1.3.0', // the expanded specifier
rawSpec: '1.2' // the specifier as passed in
}

// Parsing urls pointing at hosted git services produces a variation:
Expand All @@ -38,6 +39,7 @@ var parsed = npa("git+https://github.com/user/foo")
raw: 'git+https://github.com/user/foo',
scope: null,
name: null,
escapedName: null,
rawSpec: 'git+https://github.com/user/foo',
spec: 'user/foo',
type: 'hosted',
Expand Down Expand Up @@ -97,8 +99,11 @@ keys:
* `rawSpec` - The part after the `name@...`, as it was originally
provided.
* `scope` - If a name is something like `@org/module` then the `scope`
field will be set to `org`. If it doesn't have a scoped name, then
field will be set to `@org`. If it doesn't have a scoped name, then
scope is `null`.
* `escapedName` - The escaped version of `name`, which may be needed when
interacting with registry internals like CouchDB. When `name` is `null`,
`escapedName` will also be `null`.

If you only include a name and no specifier part, eg, `foo` or `foo@` then
a default of `latest` will be used (as of 4.1.0). This is contrast with
Expand Down
8 changes: 8 additions & 0 deletions npa.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ function npa (arg) {
var res = new Result
res.raw = arg
res.scope = null
res.escapedName = 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]
res.escapedName = escapeName(res.name)
if (nameparse[2])
res.scope = "@" + nameparse[2]
arg = arg.substr(nameparse[0].length)
Expand Down Expand Up @@ -106,6 +108,7 @@ function npa (arg) {
res.spec = "latest"
res.rawSpec = ""
res.name = arg
res.escapedName = escapeName(res.name)
if (p[1])
res.scope = "@" + p[1]
} else {
Expand All @@ -116,6 +119,11 @@ function npa (arg) {
return res
}

function escapeName (name) {
// scoped packages in couch must have slash url-encoded, e.g. @foo%2Fbar
return name && name.replace('/', '%2f')
}

function parseLocal (res, arg) {
// turns out nearly every character is allowed in fs paths
if (/\0/.test(arg)) {
Expand Down
19 changes: 19 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require("tap").test("basic", function (t) {
var tests = {
"foo@1.2": {
name: "foo",
escapedName: "foo",
type: "range",
spec: ">=1.2.0 <1.3.0",
raw: "foo@1.2",
Expand All @@ -16,6 +17,7 @@ require("tap").test("basic", function (t) {
"@foo/bar": {
raw: "@foo/bar",
name: "@foo/bar",
escapedName: "@foo%2fbar",
scope: "@foo",
rawSpec: "",
spec: "latest",
Expand All @@ -25,6 +27,7 @@ require("tap").test("basic", function (t) {
"@foo/bar@": {
raw: "@foo/bar@",
name: "@foo/bar",
escapedName: "@foo%2fbar",
scope: "@foo",
rawSpec: "",
spec: "latest",
Expand All @@ -34,6 +37,7 @@ require("tap").test("basic", function (t) {
"@foo/bar@baz": {
raw: "@foo/bar@baz",
name: "@foo/bar",
escapedName: "@foo%2fbar",
scope: "@foo",
rawSpec: "baz",
spec: "baz",
Expand All @@ -43,20 +47,23 @@ require("tap").test("basic", function (t) {
"@f fo o al/ a d s ;f ": {
raw: "@f fo o al/ a d s ;f",
name: null,
escapedName: null,
rawSpec: "@f fo o al/ a d s ;f",
spec: "@f fo o al/ a d s ;f",
type: "local"
},

"foo@1.2.3": {
name: "foo",
escapedName: "foo",
type: "version",
spec: "1.2.3",
raw: "foo@1.2.3"
},

"foo@=v1.2.3": {
name: "foo",
escapedName: "foo",
type: "version",
spec: "1.2.3",
raw: "foo@=v1.2.3",
Expand All @@ -65,27 +72,31 @@ require("tap").test("basic", function (t) {

"git+ssh://git@notgithub.com/user/foo#1.2.3": {
name: null,
escapedName: null,
type: "git",
spec: "ssh://git@notgithub.com/user/foo#1.2.3",
raw: "git+ssh://git@notgithub.com/user/foo#1.2.3"
},

"git+file://path/to/repo#1.2.3": {
name: null,
escapedName: null,
type: "git",
spec: "file://path/to/repo#1.2.3",
raw: "git+file://path/to/repo#1.2.3"
},

"git://notgithub.com/user/foo": {
name: null,
escapedName: null,
type: "git",
spec: "git://notgithub.com/user/foo",
raw: "git://notgithub.com/user/foo"
},

"@foo/bar@git+ssh://notgithub.com/user/foo": {
name: "@foo/bar",
escapedName: "@foo%2fbar",
scope: "@foo",
spec: "ssh://notgithub.com/user/foo",
rawSpec: "git+ssh://notgithub.com/user/foo",
Expand All @@ -94,55 +105,63 @@ require("tap").test("basic", function (t) {

"/path/to/foo": {
name: null,
escapedName: null,
type: "local",
spec: path.resolve(__dirname, "/path/to/foo"),
raw: "/path/to/foo"
},

"file:path/to/foo": {
name: null,
escapedName: null,
type: "local",
spec: "path/to/foo",
raw: "file:path/to/foo"
},

"file:~/path/to/foo": {
name: null,
escapedName: null,
type: "local",
spec: "~/path/to/foo",
raw: "file:~/path/to/foo"
},

"file:../path/to/foo": {
name: null,
escapedName: null,
type: "local",
spec: "../path/to/foo",
raw: "file:../path/to/foo"
},

"file:///path/to/foo": {
name: null,
escapedName: null,
type: "local",
spec: "/path/to/foo",
raw: "file:///path/to/foo"
},

"https://server.com/foo.tgz": {
name: null,
escapedName: null,
type: "remote",
spec: "https://server.com/foo.tgz",
raw: "https://server.com/foo.tgz"
},

"foo@latest": {
name: "foo",
escapedName: "foo",
type: "tag",
spec: "latest",
raw: "foo@latest"
},

"foo": {
name: "foo",
escapedName: "foo",
type: "tag",
spec: "latest",
raw: "foo"
Expand Down
7 changes: 7 additions & 0 deletions test/windows.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var cases = {
raw: "C:\\x\\y\\z",
scope: null,
name: null,
escapedName: null,
rawSpec: "C:\\x\\y\\z",
spec: "C:\\x\\y\\z",
type: "local"
Expand All @@ -17,6 +18,7 @@ var cases = {
raw: "foo@C:\\x\\y\\z",
scope: null,
name: "foo",
escapedName: "foo",
rawSpec: "C:\\x\\y\\z",
spec: "C:\\x\\y\\z",
type: "local"
Expand All @@ -25,6 +27,7 @@ var cases = {
raw: "foo@file:///C:\\x\\y\\z",
scope: null,
name: "foo",
escapedName: "foo",
rawSpec: "file:///C:\\x\\y\\z",
spec: "C:\\x\\y\\z",
type: "local"
Expand All @@ -33,6 +36,7 @@ var cases = {
raw: "foo@file://C:\\x\\y\\z",
scope: null,
name: "foo",
escapedName: "foo",
rawSpec: "file://C:\\x\\y\\z",
spec: "C:\\x\\y\\z",
type: "local"
Expand All @@ -41,6 +45,7 @@ var cases = {
raw: "file:///C:\\x\\y\\z",
scope: null,
name: null,
escapedName: null,
rawSpec: "file:///C:\\x\\y\\z",
spec: "C:\\x\\y\\z",
type: "local"
Expand All @@ -49,6 +54,7 @@ var cases = {
raw: "file://C:\\x\\y\\z",
scope: null,
name: null,
escapedName: null,
rawSpec: "file://C:\\x\\y\\z",
spec: "C:\\x\\y\\z",
type: "local"
Expand All @@ -57,6 +63,7 @@ var cases = {
raw: "foo@/foo/bar/baz",
scope: null,
name: "foo",
escapedName: "foo",
rawSpec: "/foo/bar/baz",
spec: "/foo/bar/baz",
type: "local"
Expand Down