Skip to content

Commit 06da033

Browse files
committed
Add JSDoc based types
1 parent 60948ee commit 06da033

File tree

6 files changed

+228
-126
lines changed

6 files changed

+228
-126
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
2+
*.d.ts
23
*.log
34
coverage/
45
node_modules/

index.js

+183-119
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,199 @@
1-
// See https://tools.ietf.org/html/rfc4647#section-3.1
2-
// for more information on the algorithms.
3-
4-
export var basicFilter = factory(basic, true)
5-
export var extendedFilter = factory(extended, true)
6-
export var lookup = factory(look)
7-
8-
// Basic Filtering (Section 3.3.1) matches a language priority list consisting
9-
// of basic language ranges (Section 2.1) to sets of language tags.
10-
function basic(tag, range) {
11-
return range === '*' || tag === range || tag.includes(range + '-')
12-
}
13-
14-
// Extended Filtering (Section 3.3.2) matches a language priority list
15-
// consisting of extended language ranges (Section 2.2) to sets of language
16-
// tags.
17-
function extended(tag, range) {
18-
// 3.3.2.1
19-
var left = tag.split('-')
20-
var right = range.split('-')
21-
var leftIndex = 0
22-
var rightIndex = 0
23-
24-
// 3.3.2.2
25-
if (right[rightIndex] !== '*' && left[leftIndex] !== right[rightIndex]) {
26-
return false
27-
}
1+
/**
2+
* See https://tools.ietf.org/html/rfc4647#section-3.1
3+
* for more information on the algorithms.
4+
*/
5+
6+
/**
7+
* @typedef {string} Tag
8+
* @typedef {Array.<Tag>} Tags
9+
* @typedef {string} Range
10+
* @typedef {Array.<Range>} Ranges
11+
* @typedef {function(Tag, Range): boolean} Check
12+
* @typedef {function(Tag|Tags, Range|Ranges=): Tags} Filter
13+
* @typedef {function(Tag|Tags, Range|Ranges=): Tag} Lookup
14+
*/
15+
16+
/**
17+
* Factory to perform a filter or a lookup.
18+
* This factory creates a function that accepts a list of tags and a list of
19+
* ranges, and contains logic to exit early for lookups.
20+
* `check` just has to deal with one tag and one range.
21+
* This match function iterates over ranges, and for each range,
22+
* iterates over tags. That way, earlier ranges matching any tag have
23+
* precedence over later ranges.
24+
*
25+
* @type {{
26+
* (check: Check, filter: true): Filter
27+
* (check: Check, filter?: false): Lookup
28+
* }}
29+
*/
30+
// prettier-ignore
31+
var factory = (
32+
/**
33+
* @param {Check} check
34+
* @param {boolean} [filter=false]
35+
*/
36+
function (check, filter) {
37+
return match
38+
39+
/**
40+
* @param {Tag|Tags} tags
41+
* @param {Range|Ranges} [ranges='*']
42+
* @returns {Tag|Tags}
43+
*/
44+
function match(tags, ranges) {
45+
var left = cast(tags, 'tag')
46+
var right = cast(
47+
ranges === null || ranges === undefined ? '*' : ranges,
48+
'range'
49+
)
50+
/** @type {Tags} */
51+
var matches = []
52+
var rightIndex = -1
53+
/** @type {Range} */
54+
var range
55+
/** @type {number} */
56+
var leftIndex
57+
/** @type {Tags} */
58+
var next
59+
60+
while (++rightIndex < right.length) {
61+
range = right[rightIndex].toLowerCase()
62+
63+
// Ignore wildcards in lookup mode.
64+
if (!filter && range === '*') continue
65+
66+
leftIndex = -1
67+
next = []
68+
69+
while (++leftIndex < left.length) {
70+
if (check(left[leftIndex].toLowerCase(), range)) {
71+
// Exit if this is a lookup and we have a match.
72+
if (!filter) return left[leftIndex]
73+
matches.push(left[leftIndex])
74+
} else {
75+
next.push(left[leftIndex])
76+
}
77+
}
2878

29-
leftIndex++
30-
rightIndex++
79+
left = next
80+
}
3181

32-
// 3.3.2.3
33-
while (rightIndex < right.length) {
34-
// 3.3.2.3.A
35-
if (right[rightIndex] === '*') {
36-
rightIndex++
37-
continue
82+
// If this is a filter, return the list. If it’s a lookup, we didn’t find
83+
// a match, so return `undefined`.
84+
return filter ? matches : undefined
3885
}
39-
40-
// 3.3.2.3.B
41-
if (!left[leftIndex]) return false
42-
43-
// 3.3.2.3.C
44-
if (left[leftIndex] === right[rightIndex]) {
45-
leftIndex++
46-
rightIndex++
47-
continue
86+
}
87+
)
88+
89+
/**
90+
* Basic Filtering (Section 3.3.1) matches a language priority list consisting
91+
* of basic language ranges (Section 2.1) to sets of language tags.
92+
* @param {Tag|Tags} tags
93+
* @param {Range|Ranges} [ranges]
94+
* @returns {Tags}
95+
*/
96+
export var basicFilter = factory(
97+
/** @type {Check} */
98+
function (tag, range) {
99+
return range === '*' || tag === range || tag.includes(range + '-')
100+
},
101+
true
102+
)
103+
104+
/**
105+
* Extended Filtering (Section 3.3.2) matches a language priority list
106+
* consisting of extended language ranges (Section 2.2) to sets of language
107+
* tags.
108+
* @param {Tag|Tags} tags
109+
* @param {Range|Ranges} [ranges]
110+
* @returns {Tags}
111+
*/
112+
export var extendedFilter = factory(
113+
/** @type {Check} */
114+
function (tag, range) {
115+
// 3.3.2.1
116+
var left = tag.split('-')
117+
var right = range.split('-')
118+
var leftIndex = 0
119+
var rightIndex = 0
120+
121+
// 3.3.2.2
122+
if (right[rightIndex] !== '*' && left[leftIndex] !== right[rightIndex]) {
123+
return false
48124
}
49125

50-
// 3.3.2.3.D
51-
if (left[leftIndex].length === 1) return false
52-
53-
// 3.3.2.3.E
54126
leftIndex++
55-
}
56-
57-
// 3.3.2.4
58-
return true
59-
}
60-
61-
// Lookup (Section 3.4) matches a language priority list consisting of basic
62-
// language ranges to sets of language tags to find the one exact language tag
63-
// that best matches the range.
64-
function look(tag, range) {
65-
var right = range
66-
var index
67-
68-
/* eslint-disable-next-line no-constant-condition */
69-
while (true) {
70-
if (right === '*' || tag === right) return true
71-
72-
index = right.lastIndexOf('-')
73-
74-
if (index < 0) return false
75-
76-
if (right.charAt(index - 2) === '-') index -= 2
127+
rightIndex++
128+
129+
// 3.3.2.3
130+
while (rightIndex < right.length) {
131+
// 3.3.2.3.A
132+
if (right[rightIndex] === '*') {
133+
rightIndex++
134+
continue
135+
}
77136

78-
right = right.slice(0, index)
79-
}
80-
}
137+
// 3.3.2.3.B
138+
if (!left[leftIndex]) return false
81139

82-
// Factory to perform a filter or a lookup.
83-
// This factory creates a function that accepts a list of tags and a list of
84-
// ranges, and contains logic to exit early for lookups.
85-
// `check` just has to deal with one tag and one range.
86-
// This match function iterates over ranges, and for each range,
87-
// iterates over tags. That way, earlier ranges matching any tag have
88-
// precedence over later ranges.
89-
function factory(check, filter) {
90-
return match
91-
92-
function match(tags, ranges) {
93-
var left = cast(tags, 'tag')
94-
var right = cast(
95-
ranges === null || ranges === undefined ? '*' : ranges,
96-
'range'
97-
)
98-
var matches = []
99-
var rightIndex = -1
100-
var range
101-
var leftIndex
102-
var next
103-
104-
while (++rightIndex < right.length) {
105-
range = right[rightIndex].toLowerCase()
106-
107-
// Ignore wildcards in lookup mode.
108-
if (!filter && range === '*') continue
109-
110-
leftIndex = -1
111-
next = []
112-
113-
while (++leftIndex < left.length) {
114-
if (check(left[leftIndex].toLowerCase(), range)) {
115-
// Exit if this is a lookup and we have a match.
116-
if (!filter) return left[leftIndex]
117-
matches.push(left[leftIndex])
118-
} else {
119-
next.push(left[leftIndex])
120-
}
140+
// 3.3.2.3.C
141+
if (left[leftIndex] === right[rightIndex]) {
142+
leftIndex++
143+
rightIndex++
144+
continue
121145
}
122146

123-
left = next
147+
// 3.3.2.3.D
148+
if (left[leftIndex].length === 1) return false
149+
150+
// 3.3.2.3.E
151+
leftIndex++
124152
}
125153

126-
// If this is a filter, return the list. If it’s a lookup, we didn’t find
127-
// a match, so return `undefined`.
128-
return filter ? matches : undefined
154+
// 3.3.2.4
155+
return true
156+
},
157+
true
158+
)
159+
160+
/**
161+
* Lookup (Section 3.4) matches a language priority list consisting of basic
162+
* language ranges to sets of language tags to find the one exact language tag
163+
* that best matches the range.
164+
* @param {Tag|Tags} tags
165+
* @param {Range|Ranges} [ranges]
166+
* @returns {Tag}
167+
*/
168+
export var lookup = factory(
169+
/** @type {Check} */
170+
function (tag, range) {
171+
var right = range
172+
/** @type {number} */
173+
var index
174+
175+
/* eslint-disable-next-line no-constant-condition */
176+
while (true) {
177+
if (right === '*' || tag === right) return true
178+
179+
index = right.lastIndexOf('-')
180+
181+
if (index < 0) return false
182+
183+
if (right.charAt(index - 2) === '-') index -= 2
184+
185+
right = right.slice(0, index)
186+
}
129187
}
130-
}
131-
132-
// Validate tags or ranges, and cast them to arrays.
188+
)
189+
190+
/**
191+
* Validate tags or ranges, and cast them to arrays.
192+
*
193+
* @param {string|Array.<string>} values
194+
* @param {string} name
195+
* @returns {Array.<string>}
196+
*/
133197
function cast(values, name) {
134198
var value = values && typeof values === 'string' ? [values] : values
135199

package.json

+14-1
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,31 @@
2929
"sideEffects": false,
3030
"type": "module",
3131
"main": "index.js",
32+
"types": "index.d.ts",
3233
"files": [
34+
"index.d.ts",
3335
"index.js"
3436
],
3537
"devDependencies": {
38+
"@types/tape": "^4.0.0",
3639
"c8": "^7.0.0",
3740
"chalk": "^4.0.0",
3841
"prettier": "^2.0.0",
3942
"remark-cli": "^9.0.0",
4043
"remark-preset-wooorm": "^8.0.0",
44+
"rimraf": "^3.0.0",
4145
"tape": "^5.0.0",
46+
"type-coverage": "^2.0.0",
47+
"typescript": "^4.0.0",
4248
"xo": "^0.38.0"
4349
},
4450
"scripts": {
51+
"prepack": "npm run build && npm run format",
52+
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
4553
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
4654
"test-api": "node test.js",
4755
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
48-
"test": "npm run format && npm run test-coverage"
56+
"test": "npm run build && npm run format && npm run test-coverage"
4957
},
5058
"prettier": {
5159
"tabWidth": 2,
@@ -77,5 +85,10 @@
7785
false
7886
]
7987
]
88+
},
89+
"typeCoverage": {
90+
"atLeast": 100,
91+
"detail": true,
92+
"strict": true
8093
}
8194
}

0 commit comments

Comments
 (0)