Skip to content

Commit

Permalink
feat: add function to return pacakge purl
Browse files Browse the repository at this point in the history
Co-authored-by: Jordan Harband <ljharb@gmail.com>
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
2 people authored and wraithgar committed Dec 1, 2022
1 parent 103c0fd commit f2c243c
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ included then the default is `latest`.

**Throws** if the package name is invalid, a dist-tag is invalid or a URL's protocol is not supported.

### var purl = npa.toPurl(*arg*, *reg*)

Returns the [purl (package URL)](https://github.com/package-url/purl-spec) form of the given pacakge name/spec.

* *arg* - A package/version string. For example: `foo@1.0.0` or `@bar/foo@2.0.0-alpha.1`.
* *reg* - Optionally the URL to the package registry. If not specified, assumes the default
`https://registry.npmjs.org`.

**Throws** if the package name is invalid, or the supplied arg can't be resolved to a purl.

## RESULT OBJECT

The objects that are returned by npm-package-arg contain the following
Expand Down
26 changes: 26 additions & 0 deletions lib/npa.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'
module.exports = npa
module.exports.resolve = resolve
module.exports.toPurl = toPurl
module.exports.Result = Result

const url = require('url')
Expand Down Expand Up @@ -87,6 +88,24 @@ function resolve (name, spec, where, arg) {
}
}

const defaultRegistry = 'https://registry.npmjs.org'

function toPurl (arg, reg = defaultRegistry) {
const res = npa(arg)

if (res.type !== 'version') {
throw invalidPurlType(res.type, res.raw)
}

// URI-encode leading @ of scoped packages
let purl = 'pkg:npm/' + res.name.replace(/^@/, '%40') + '@' + res.rawSpec
if (reg !== defaultRegistry) {
purl += '?repository_url=' + reg
}

return purl
}

function invalidPackageName (name, valid, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid package name "${name}" of package "${raw}": ${valid.errors.join('; ')}.`)
Expand All @@ -101,6 +120,13 @@ function invalidTagName (name, raw) {
return err
}

function invalidPurlType (type, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid type "${type}" of package "${raw}": Purl can only be generated for "version" types.`)
err.code = 'EINVALIDPURLTYPE'
return err
}

function Result (opts) {
this.type = opts.type
this.registry = opts.registry
Expand Down
46 changes: 46 additions & 0 deletions test/purl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict'
var test = require('tap').test
var npa = require('..')

test('toPurl - valid', function (t) {
// Unscoped package
t.equal(npa.toPurl('foo@1.2.3'), 'pkg:npm/foo@1.2.3')

// Scoped package
t.equal(
npa.toPurl('@foo/bar@1.2.3-alpha.1'),
'pkg:npm/%40foo/bar@1.2.3-alpha.1')

// Non-default registry
t.equal(
npa.toPurl({ name: '@foo/bar', rawSpec: '1.0.0' }, 'https://npm.pkg.github.com'),
'pkg:npm/%40foo/bar@1.0.0?repository_url=https://npm.pkg.github.com'
)

// Default registry
t.equal(
npa.toPurl({ name: '@foo/bar', rawSpec: '1.0.0' }, 'https://registry.npmjs.org'),
'pkg:npm/%40foo/bar@1.0.0'
)

t.end()
})

test('toPurl - invalid', function (t) {
// Invalid version
t.throws(() => npa.toPurl({ name: 'foo/bar', rawSpec: '1.0.0' }), {
code: 'EINVALIDPACKAGENAME',
})

// Invalid version
t.throws(() => npa.toPurl('foo@a.b.c'), {
code: 'EINVALIDPURLTYPE',
})

// Invalid type
t.throws(() => npa.toPurl('git+ssh://git@github.com/user/foo#1.2.3'), {
code: 'EINVALIDPURLTYPE',
})

t.end()
})

0 comments on commit f2c243c

Please sign in to comment.