Skip to content

Commit b5660a5

Browse files
authored
Merge pull request #73 from package-url/jdalton/sync
Additional feat, chore, and fixes
2 parents 400de0c + b6c8ce8 commit b5660a5

19 files changed

+1260
-956
lines changed

README.md

+97-38
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,75 @@
11
# packageurl-js
22

3-
### Installing:
3+
### Installing
4+
45
To install `packageurl-js` in your project, simply run:
5-
```
6+
```bash
67
npm install packageurl-js
78
```
89

910
This command will download the `packageurl-js` npm package for use in your application.
1011

11-
### Local Development:
12-
Clone the `packageurl-js` repo and `cd` into the directory.
12+
### Local Development
1313

14-
Then run:
15-
```
14+
Clone the `packageurl-js` repo and `cd` into the directory.
15+
16+
Then run:
17+
```bash
1618
npm install
1719
```
1820

1921
### Testing
20-
To run the test suite:
21-
```
22+
23+
To run the test suite:
24+
```bash
2225
npm test
2326
```
2427

2528
### Usage Examples
2629

27-
#### Import ES6 Module
30+
#### Importing
2831

32+
As an ES6 module
33+
```js
34+
import { PackageURL } from 'packageurl-js'
2935
```
30-
import { PackageURL } from 'packageurl-js';
31-
```
32-
33-
#### Import CommonJs Module
3436

35-
```
36-
const { PackageURL } = require('packageurl-js');
37+
As a CommonJS module
38+
```js
39+
const { PackageURL } = require('packageurl-js')
3740
```
3841

39-
#### Parsing from a string
42+
#### Parsing
4043

41-
```
42-
const pkg = PackageURL.fromString('pkg:maven/org.springframework.integration/[email protected]');
43-
console.log(pkg);
44+
```js
45+
const purlStr = 'pkg:maven/org.springframework.integration/[email protected]'
46+
console.log(PackageURL.fromString(purlStr))
47+
console.log(new PackageURL(...PackageURL.parseString(purlStr)))
4448
```
4549

46-
=>
50+
will both log
4751

4852
```
4953
PackageURL {
50-
type: 'maven',
51-
name: 'spring-integration-jms',
52-
namespace: 'org.springframework.integration',
53-
version: '5.5.5',
54-
qualifiers: null,
55-
subpath: null
54+
type: 'maven',
55+
name: 'spring-integration-jms',
56+
namespace: 'org.springframework.integration',
57+
version: '5.5.5',
58+
qualifiers: undefined,
59+
subpath: undefined
5660
}
5761
```
5862

5963
#### Constructing
6064

61-
```
65+
```js
6266
const pkg = new PackageURL(
6367
'maven',
6468
'org.springframework.integration',
6569
'spring-integration-jms',
66-
'5.5.5',
67-
undefined,
68-
undefined);
69-
70-
console.log(pkg.toString());
70+
'5.5.5'
71+
)
72+
console.log(pkg.toString())
7173
```
7274

7375
=>
@@ -78,16 +80,73 @@ pkg:maven/org.springframework.integration/[email protected]
7880

7981
#### Error Handling
8082

81-
```
83+
```js
8284
try {
83-
PackageURL.fromString('not-a-purl');
84-
} catch(ex) {
85-
console.error(ex.message);
85+
PackageURL.fromString('not-a-purl')
86+
} catch (e) {
87+
console.error(e.message)
8688
}
8789
```
8890

8991
=>
9092

9193
```
92-
purl is missing the required "pkg" scheme component.
93-
```
94+
Invalid purl: missing required "pkg" scheme component
95+
```
96+
97+
#### Helper Objects
98+
99+
Helpers for encoding, normalizing, and validating purl components and types can
100+
be imported directly from the module or found on the PackageURL class as static
101+
properties.
102+
```js
103+
import {
104+
PackageURL,
105+
PurlComponent,
106+
PurlType
107+
} from 'packageurl-js'
108+
109+
PurlComponent === PackageURL.Component // => true
110+
PurlType === PackageURL.Type // => true
111+
```
112+
113+
#### PurlComponent
114+
115+
Contains the following properties each with their own `encode`, `normalize`,
116+
and `validate` methods, e.g. `PurlComponent.name.validate(nameStr)`:
117+
- type
118+
- namespace
119+
- name
120+
- version
121+
- qualifiers
122+
- qualifierKey
123+
- qualifierValue
124+
- subpath
125+
126+
#### PurlType
127+
128+
Contains the following properties each with their own `normalize`, and `validate`
129+
methods, e.g. `PurlType.npm.validate(purlObj)`:
130+
- alpm
131+
- apk
132+
- bitbucket
133+
- bitnami
134+
- composer
135+
- conan
136+
- cran
137+
- deb
138+
- github
139+
- gitlab
140+
- golang
141+
- hex
142+
- huggingface
143+
- luarocks
144+
- maven
145+
- mlflow
146+
- npm
147+
- oci
148+
- pub
149+
- pypi
150+
- qpkg
151+
- rpm
152+
- swift

index.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ SOFTWARE.
2222
'use strict'
2323

2424
const {
25-
Component,
26-
KnownQualifierNames,
2725
PackageURL,
28-
Type
26+
PurlComponent,
27+
PurlQualifierNames,
28+
PurlType
2929
} = require('./src/package-url')
3030

3131
module.exports = {
32-
Component,
33-
KnownQualifierNames,
3432
PackageURL,
35-
Type
33+
PurlComponent,
34+
PurlQualifierNames,
35+
PurlType
3636
}

src/constants.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict'
2+
3+
const LOOP_SENTINEL = 1_000_000
4+
5+
module.exports = {
6+
LOOP_SENTINEL
7+
}

src/encode.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict'
2+
3+
const { isObject } = require('./objects')
4+
const { isNonEmptyString } = require('./strings')
5+
6+
const reusedSearchParams = new URLSearchParams()
7+
const reusedSearchParamKey = '_'
8+
const reusedSearchParamOffset = 2 // '_='.length
9+
10+
const { encodeURIComponent } = globalThis
11+
12+
function encodeNamespace(namespace) {
13+
return isNonEmptyString(namespace)
14+
? encodeURIComponent(namespace)
15+
.replace(/%3A/g, ':')
16+
.replace(/%2F/g, '/')
17+
: ''
18+
}
19+
20+
function encodeQualifierParam(param) {
21+
if (isNonEmptyString(param)) {
22+
// Param key and value are encoded with `percentEncodeSet` of
23+
// 'application/x-www-form-urlencoded' and `spaceAsPlus` of `true`.
24+
// https://url.spec.whatwg.org/#urlencoded-serializing
25+
reusedSearchParams.set(reusedSearchParamKey, param)
26+
return replacePlusSignWithPercentEncodedSpace(
27+
reusedSearchParams.toString().slice(reusedSearchParamOffset)
28+
)
29+
}
30+
return ''
31+
}
32+
33+
function encodeQualifiers(qualifiers) {
34+
if (isObject(qualifiers)) {
35+
// Sort this list of qualifier strings lexicographically.
36+
const qualifiersKeys = Object.keys(qualifiers).sort()
37+
const searchParams = new URLSearchParams()
38+
for (let i = 0, { length } = qualifiersKeys; i < length; i += 1) {
39+
const key = qualifiersKeys[i]
40+
searchParams.set(key, qualifiers[key])
41+
}
42+
return replacePlusSignWithPercentEncodedSpace(searchParams.toString())
43+
}
44+
return ''
45+
}
46+
47+
function encodeSubpath(subpath) {
48+
return isNonEmptyString(subpath)
49+
? encodeURIComponent(subpath).replace(/%2F/g, '/')
50+
: ''
51+
}
52+
53+
function encodeVersion(version) {
54+
return isNonEmptyString(version)
55+
? encodeURIComponent(version).replace(/%3A/g, ':').replace(/%2B/g, '+')
56+
: ''
57+
}
58+
59+
function replacePlusSignWithPercentEncodedSpace(str) {
60+
// Convert plus signs to %20 for better portability.
61+
return str.replace(/\+/g, '%20')
62+
}
63+
64+
module.exports = {
65+
encodeNamespace,
66+
encodeVersion,
67+
encodeQualifiers,
68+
encodeQualifierParam,
69+
encodeSubpath,
70+
encodeURIComponent
71+
}

src/helpers.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict'
2+
3+
function createHelpersNamespaceObject(helpers, options_ = {}) {
4+
const { comparator, ...defaults } = { __proto__: null, ...options_ }
5+
const helperNames = Object.keys(helpers).sort()
6+
const propNames = [
7+
...new Set([...Object.values(helpers)].map(Object.keys).flat())
8+
].sort(comparator)
9+
const nsObject = Object.create(null)
10+
for (let i = 0, { length } = propNames; i < length; i += 1) {
11+
const propName = propNames[i]
12+
const helpersForProp = Object.create(null)
13+
for (
14+
let j = 0, { length: length_j } = helperNames;
15+
j < length_j;
16+
j += 1
17+
) {
18+
const helperName = helperNames[j]
19+
const helperValue =
20+
helpers[helperName][propName] ?? defaults[helperName]
21+
if (helperValue !== undefined) {
22+
helpersForProp[helperName] = helperValue
23+
}
24+
}
25+
nsObject[propName] = helpersForProp
26+
}
27+
return nsObject
28+
}
29+
30+
module.exports = {
31+
createHelpersNamespaceObject
32+
}

src/lang.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict'
2+
3+
function isNullishOrEmptyString(value) {
4+
return (
5+
value === null ||
6+
value === undefined ||
7+
(typeof value === 'string' && value.length === 0)
8+
)
9+
}
10+
11+
module.exports = {
12+
isNullishOrEmptyString
13+
}

0 commit comments

Comments
 (0)