-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathutils.js
More file actions
155 lines (137 loc) · 4.66 KB
/
utils.js
File metadata and controls
155 lines (137 loc) · 4.66 KB
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import punycode from 'punycode';
export const PREFIX_REGEX = '@';
export const PREFIX_GLOB = '!';
export const qs = (selector, node) => (node || document).querySelector(selector);
export const qsAll = (selector, node) => (node || document).querySelectorAll(selector);
export const ce = (tagName) => document.createElement(tagName);
export const cleanHostInput = (value = '') => value.trim().toLowerCase();
const getDomain = (src = '') => src.split('/')[0];
const getPath = (src = '') => src.replace(/^.+?\//, '');
const domainLength = (map) => getDomain(map).replace('*.', '').split('.').length;
const pathLength = (map) => getPath(map).replace('/*', '').split('/').length;
export const sortMaps = (maps) => maps.sort((map1, map2) => {
const d1 = domainLength(map1.host);
const d2 = domainLength(map2.host);
const p1 = pathLength(map1.host);
const p2 = pathLength(map2.host);
if (d1 === d2 && p1 === p2) return 0;
return ((d1 === d2) ? (p1 < p2) : (d1 < d2)) ? 1 : -1;
});
export const domainMatch = (url, map) => {
const url_host = getDomain(url);
const map_host = getDomain(map);
if (map_host.slice(0, 2) !== '*.') return url_host === map_host;
// Check wildcard matches in reverse order (com.example.*)
const wild_url = url_host.split('.').reverse();
const wild_map = map_host.slice(2).split('.').reverse();
if (wild_url.length < wild_map.length) return false;
for (let i = 0; i < wild_map.length; ++i)
if (wild_url[i] !== wild_map[i]) return false;
return true;
};
export const pathMatch = (url, map) => {
const url_path = getPath(url);
const map_path = getPath(map);
if (map_path === '*' || map_path === '') return true;
// Paths are always wild
const wild_url = url_path.split('/');
const wild_map = map_path.replace('/*', '').split('/');
if (wild_url.length < wild_map.length) return false;
for (let i = 0; i < wild_map.length; ++i)
if (wild_url[i] !== wild_map[i]) return false;
return true;
};
/**
*
* @param url {URL}
* @return {string}
*/
export const urlKeyFromUrl = (url) => {
return punycode.toUnicode(url.hostname.replace('www.', '')) + url.pathname;
};
/**
* Checks if the URL matches a given hostmap
*
* Depending on the prefix in the hostmap it'll choose a match method:
* - regex
* - glob
* - standard
*
* @param url {String}
* @param matchDomainOnly {Boolean=}
* @param map
* @return {*}
*/
export const matchesSavedMap = (url, matchDomainOnly, {host}) => {
let toMatch = url;
let urlO = new window.URL(url);
if (matchDomainOnly) {
toMatch = urlO.host;
urlO = new window.URL(`${urlO.protocol}//${urlO.host}`);
}
if (host[0] === PREFIX_REGEX) {
let regex = host.substr(1);
if (matchDomainOnly) {
// This might generate double ^^ characters, but that works anyway
regex = "^" + regex + "$";
}
try {
return new RegExp(regex).test(toMatch);
} catch (e) {
console.error('couldn\'t test regex', regex, e);
}
} else if (host[0] === PREFIX_GLOB) {
// turning glob into regex isn't the worst thing:
// 1. * becomes .*
// 2. ? becomes .?
// Because the string is regex escaped, you must match \* to instead of *
let regex = escapeRegExp(host.substr(1))
.replace(/\\\*/g, '.*')
.replace(/\\\?/g, '.?')
if (matchDomainOnly) {
regex = "^" + regex + "$";
}
return new RegExp(regex).test(toMatch);
} else {
const key = urlKeyFromUrl(urlO);
const _url = ((key.indexOf('/') === -1) ? key.concat('/') : key).toLowerCase();
const mapHost = ((host.indexOf('/') === -1) ? host.concat('/') : host).toLowerCase();
return domainMatch(_url, mapHost) && pathMatch(_url, mapHost);
}
};
export const filterByKey = (dict, func) => {
return Object.keys(dict)
.filter(func)
.reduce((acc, curr) => {
acc[curr] = dict[curr];
return acc;
}, {});
};
/**
* Replaces occurrences of {variable} in strings
*
* It handles camelCase, kebab-case and snake_case variable names
*
* @param string {String}
* @param context {Object}
* @throws Error when the variable doesn't exist in the context
* @return {String}
*/
export function formatString(string, context) {
return string.replace(/(\{([\w_-]+)\})/g, (match, _, token) => {
const replacement = context[token];
if (replacement === undefined) {
throw `Cannot find variable '${token}' in context`;
}
return replacement;
});
}
/**
* Escape all regex metacharacters in a string
*
* @param string {String}
*/
function escapeRegExp(string) {
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}