-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
103 lines (91 loc) · 2.96 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
const defaultOptions = {
delimiter: ' ',
end: true,
variableIndicator: ':',
optionalIndicator: '?'
}
module.exports = function compileMatch(tPath, tOptions = {}) {
// fill unfilled options
const options = {}
Object.assign(options, defaultOptions, tOptions)
// prevent reassigning function parameters
let path = tPath
// remove leading and trailing delimiters
if (path.startsWith(options.delimiter)) {
path = path.slice(options.delimiter.length)
}
if (path.endsWith(options.delimiter)) {
path = path.slice(0, -1 * options.delimiter.length)
}
// create array of path parts
const pathParts = path.split(options.delimiter)
const variables = []
for (const part of pathParts) {
if (part.startsWith(options.variableIndicator)) {
variables.push({
name: part.slice(options.variableIndicator.length),
index: pathParts.indexOf(part),
optional: false
})
}
if (part.startsWith(options.optionalIndicator)) {
variables.push({
name: part.slice(options.optionalIndicator.length),
index: pathParts.indexOf(part),
optional: true
})
}
}
// check that all optional variables are at the end
let lastNonOptIndex = 0
let firstOptIndex = Infinity
for (let i = 0; i < pathParts.length; i++) {
const part = pathParts[i]
if (!part.startsWith(options.optionalIndicator) && i > lastNonOptIndex) {
lastNonOptIndex = i
}
if (part.startsWith(options.optionalIndicator) && i < firstOptIndex) {
firstOptIndex = i
}
}
if (lastNonOptIndex > firstOptIndex) {
throw new Error('path-to-matcher: Optional variables must be defined at ' +
'the end of the path')
}
const isEqual = module.exports.equal.bind(null, options)
function matcher(tStr) {
let str = tStr
// remove leading and trailing delimiters
if (str.startsWith(options.delimiter)) {
str = str.slice(options.delimiter.length)
}
if (str.endsWith(options.delimiter)) {
str = str.slice(0, -1 * options.delimiter.length)
}
// create array of parts in string
const strParts = str.split(options.delimiter)
if (options.end && strParts.length > pathParts.length) {
return false
}
// init return values
const match = isEqual(pathParts, strParts)
const vars = Object.create(null)
// fill vars object with matched variables
for (const variable of variables) {
vars[variable.name] = strParts[variable.index] || null
}
return { match, vars }
}
return Object.assign(matcher, { pathParts, variables })
}
// define equal function with access to passed options
module.exports.equal = function equal(options, a, b) {
for (let i = 0; i < a.length; i++) {
const condition = a[i].startsWith(options.optionalIndicator) ? true : !!b[i]
if (!condition) return false
if (!a[i].startsWith(options.variableIndicator) && !a[i].startsWith(options.optionalIndicator)) {
if (a[i] !== b[i]) return false
}
}
return true
}