bcp-47-match
Advanced tools
Comparing version 2.0.1 to 2.0.2
export function basicFilter( | ||
tag: Tag | Tags, | ||
ranges?: string | Ranges | undefined | ||
): Tag | ||
tags: string | Tags, | ||
ranges: string | Ranges | undefined | ||
): Tags | ||
export function extendedFilter( | ||
tag: Tag | Tags, | ||
ranges?: string | Ranges | undefined | ||
): Tag | ||
tags: string | Tags, | ||
ranges: string | Ranges | undefined | ||
): Tags | ||
export function lookup( | ||
tag: Tag | Tags, | ||
ranges?: string | Ranges | undefined | ||
): Tag | ||
tags: string | Tags, | ||
ranges: string | Ranges | undefined | ||
): string | undefined | ||
export type Tag = string | ||
@@ -18,9 +18,7 @@ export type Tags = Array<Tag> | ||
export type Check = (tag: Tag, range: Range) => boolean | ||
export type Filter = ( | ||
tag: Tag | Tags, | ||
export type Filter = FilterOrLookup<true> | ||
export type Lookup = FilterOrLookup<false> | ||
export type FilterOrLookup<IsFilter extends boolean> = ( | ||
tags: Tag | Tags, | ||
ranges?: string | Ranges | undefined | ||
) => Tag | ||
export type Lookup = ( | ||
tag: Tag | Tags, | ||
ranges?: string | Ranges | undefined | ||
) => Tag | ||
) => IsFilter extends true ? Tags : Tag | undefined |
226
index.js
@@ -11,5 +11,3 @@ /** | ||
* @typedef {Array<Range>} Ranges | ||
*/ | ||
/** | ||
* | ||
* @callback Check | ||
@@ -19,19 +17,16 @@ * @param {Tag} tag | ||
* @returns {boolean} | ||
* | ||
* @typedef {FilterOrLookup<true>} Filter | ||
* @typedef {FilterOrLookup<false>} Lookup | ||
*/ | ||
/** | ||
* @callback Filter | ||
* @param {Tag|Tags} tag | ||
* @param {Range|Ranges} [ranges] | ||
* @returns {Tag} | ||
* @template {boolean} IsFilter | ||
* @callback FilterOrLookup | ||
* @param {Tag|Tags} tags | ||
* @param {Range|Ranges} [ranges='*'] | ||
* @returns {IsFilter extends true ? Tags : Tag|undefined} | ||
*/ | ||
/** | ||
* @callback Lookup | ||
* @param {Tag|Tags} tag | ||
* @param {Range|Ranges} [ranges] | ||
* @returns {Tag} | ||
*/ | ||
/** | ||
* Factory to perform a filter or a lookup. | ||
@@ -45,60 +40,53 @@ * This factory creates a function that accepts a list of tags and a list of | ||
* | ||
* @type {{ | ||
* (check: Check, filter: true): Filter | ||
* (check: Check, filter?: false): Lookup | ||
* }} | ||
* @template {boolean} IsFilter | ||
* @param {Check} check | ||
* @param {IsFilter} filter | ||
* @returns {FilterOrLookup<IsFilter>} | ||
*/ | ||
// prettier-ignore | ||
const factory = ( | ||
/** | ||
* @param {Check} check | ||
* @param {boolean} [filter=false] | ||
*/ | ||
function (check, filter) { | ||
return match | ||
function factory(check, filter) { | ||
return function (tags, ranges) { | ||
let left = cast(tags, 'tag') | ||
const right = cast( | ||
ranges === null || ranges === undefined ? '*' : ranges, | ||
'range' | ||
) | ||
/** @type {Tags} */ | ||
const matches = [] | ||
let rightIndex = -1 | ||
/** | ||
* @param {Tag|Tags} tags | ||
* @param {Range|Ranges} [ranges='*'] | ||
* @returns {Tag|Tags|undefined} | ||
*/ | ||
function match(tags, ranges) { | ||
let left = cast(tags, 'tag') | ||
const right = cast( | ||
ranges === null || ranges === undefined ? '*' : ranges, | ||
'range' | ||
) | ||
/** @type {Tags} */ | ||
const matches = [] | ||
let rightIndex = -1 | ||
while (++rightIndex < right.length) { | ||
const range = right[rightIndex].toLowerCase() | ||
while (++rightIndex < right.length) { | ||
const range = right[rightIndex].toLowerCase() | ||
// Ignore wildcards in lookup mode. | ||
if (!filter && range === '*') continue | ||
// Ignore wildcards in lookup mode. | ||
if (!filter && range === '*') continue | ||
let leftIndex = -1 | ||
/** @type {Tags} */ | ||
const next = [] | ||
let leftIndex = -1 | ||
/** @type {Tags} */ | ||
const next = [] | ||
while (++leftIndex < left.length) { | ||
if (check(left[leftIndex].toLowerCase(), range)) { | ||
// Exit if this is a lookup and we have a match. | ||
if (!filter) { | ||
return /** @type {IsFilter extends true ? Tags : Tag|undefined} */ ( | ||
left[leftIndex] | ||
) | ||
} | ||
while (++leftIndex < left.length) { | ||
if (check(left[leftIndex].toLowerCase(), range)) { | ||
// Exit if this is a lookup and we have a match. | ||
if (!filter) return left[leftIndex] | ||
matches.push(left[leftIndex]) | ||
} else { | ||
next.push(left[leftIndex]) | ||
} | ||
matches.push(left[leftIndex]) | ||
} else { | ||
next.push(left[leftIndex]) | ||
} | ||
left = next | ||
} | ||
// If this is a filter, return the list. If it’s a lookup, we didn’t find | ||
// a match, so return `undefined`. | ||
return filter ? matches : undefined | ||
left = next | ||
} | ||
// If this is a filter, return the list. If it’s a lookup, we didn’t find | ||
// a match, so return `undefined`. | ||
return /** @type {IsFilter extends true ? Tags : Tag|undefined} */ ( | ||
filter ? matches : undefined | ||
) | ||
} | ||
) | ||
} | ||
@@ -108,13 +96,6 @@ /** | ||
* of basic language ranges (Section 2.1) to sets of language tags. | ||
* @param {Tag|Tags} tags | ||
* @param {Range|Ranges} [ranges] | ||
* @returns {Tags} | ||
*/ | ||
export const basicFilter = factory( | ||
/** @type {Check} */ | ||
function (tag, range) { | ||
return range === '*' || tag === range || tag.includes(range + '-') | ||
}, | ||
true | ||
) | ||
export const basicFilter = factory(function (tag, range) { | ||
return range === '*' || tag === range || tag.includes(range + '-') | ||
}, true) | ||
@@ -125,54 +106,47 @@ /** | ||
* tags. | ||
* @param {Tag|Tags} tags | ||
* @param {Range|Ranges} [ranges] | ||
* @returns {Tags} | ||
*/ | ||
export const extendedFilter = factory( | ||
/** @type {Check} */ | ||
function (tag, range) { | ||
// 3.3.2.1 | ||
const left = tag.split('-') | ||
const right = range.split('-') | ||
let leftIndex = 0 | ||
let rightIndex = 0 | ||
export const extendedFilter = factory(function (tag, range) { | ||
// 3.3.2.1 | ||
const left = tag.split('-') | ||
const right = range.split('-') | ||
let leftIndex = 0 | ||
let rightIndex = 0 | ||
// 3.3.2.2 | ||
if (right[rightIndex] !== '*' && left[leftIndex] !== right[rightIndex]) { | ||
return false | ||
} | ||
// 3.3.2.2 | ||
if (right[rightIndex] !== '*' && left[leftIndex] !== right[rightIndex]) { | ||
return false | ||
} | ||
leftIndex++ | ||
rightIndex++ | ||
leftIndex++ | ||
rightIndex++ | ||
// 3.3.2.3 | ||
while (rightIndex < right.length) { | ||
// 3.3.2.3.A | ||
if (right[rightIndex] === '*') { | ||
rightIndex++ | ||
continue | ||
} | ||
// 3.3.2.3 | ||
while (rightIndex < right.length) { | ||
// 3.3.2.3.A | ||
if (right[rightIndex] === '*') { | ||
rightIndex++ | ||
continue | ||
} | ||
// 3.3.2.3.B | ||
if (!left[leftIndex]) return false | ||
// 3.3.2.3.B | ||
if (!left[leftIndex]) return false | ||
// 3.3.2.3.C | ||
if (left[leftIndex] === right[rightIndex]) { | ||
leftIndex++ | ||
rightIndex++ | ||
continue | ||
} | ||
// 3.3.2.3.D | ||
if (left[leftIndex].length === 1) return false | ||
// 3.3.2.3.E | ||
// 3.3.2.3.C | ||
if (left[leftIndex] === right[rightIndex]) { | ||
leftIndex++ | ||
rightIndex++ | ||
continue | ||
} | ||
// 3.3.2.4 | ||
return true | ||
}, | ||
true | ||
) | ||
// 3.3.2.3.D | ||
if (left[leftIndex].length === 1) return false | ||
// 3.3.2.3.E | ||
leftIndex++ | ||
} | ||
// 3.3.2.4 | ||
return true | ||
}, true) | ||
/** | ||
@@ -182,25 +156,19 @@ * Lookup (Section 3.4) matches a language priority list consisting of basic | ||
* that best matches the range. | ||
* @param {Tag|Tags} tags | ||
* @param {Range|Ranges} [ranges] | ||
* @returns {Tag} | ||
*/ | ||
export const lookup = factory( | ||
/** @type {Check} */ | ||
function (tag, range) { | ||
let right = range | ||
export const lookup = factory(function (tag, range) { | ||
let right = range | ||
/* eslint-disable-next-line no-constant-condition */ | ||
while (true) { | ||
if (right === '*' || tag === right) return true | ||
/* eslint-disable-next-line no-constant-condition */ | ||
while (true) { | ||
if (right === '*' || tag === right) return true | ||
let index = right.lastIndexOf('-') | ||
let index = right.lastIndexOf('-') | ||
if (index < 0) return false | ||
if (index < 0) return false | ||
if (right.charAt(index - 2) === '-') index -= 2 | ||
if (right.charAt(index - 2) === '-') index -= 2 | ||
right = right.slice(0, index) | ||
} | ||
right = right.slice(0, index) | ||
} | ||
) | ||
}, false) | ||
@@ -207,0 +175,0 @@ /** |
{ | ||
"name": "bcp-47-match", | ||
"version": "2.0.1", | ||
"version": "2.0.2", | ||
"description": "Match BCP 47 language tags with language ranges per RFC 4647", | ||
@@ -40,3 +40,3 @@ "license": "MIT", | ||
"c8": "^7.0.0", | ||
"chalk": "^4.0.0", | ||
"chalk": "^5.0.0", | ||
"prettier": "^2.0.0", | ||
@@ -49,3 +49,3 @@ "remark-cli": "^10.0.0", | ||
"typescript": "^4.0.0", | ||
"xo": "^0.46.0" | ||
"xo": "^0.48.0" | ||
}, | ||
@@ -52,0 +52,0 @@ "scripts": { |
@@ -50,13 +50,13 @@ <!--lint disable no-html--> | ||
In Deno with [Skypack][]: | ||
In Deno with [`esm.sh`][esmsh]: | ||
```js | ||
import * as bcp47Match from 'https://cdn.skypack.dev/bcp-47-match@2?dts' | ||
import * as bcp47Match from 'https://esm.sh/bcp-47-match@2' | ||
``` | ||
In browsers with [Skypack][]: | ||
In browsers with [`esm.sh`][esmsh]: | ||
```html | ||
<script type="module"> | ||
import * as bcp47Match from 'https://cdn.skypack.dev/bcp-47-match@2?min' | ||
import * as bcp47Match from 'https://esm.sh/bcp-47-match@2?bundle' | ||
</script> | ||
@@ -297,3 +297,3 @@ ``` | ||
[skypack]: https://www.skypack.dev | ||
[esmsh]: https://esm.sh | ||
@@ -300,0 +300,0 @@ [license]: license |
17992
180