parse-domain 2.3.1 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/build/tries/icann.complete.json +1 -1
- package/build/tries/icann.light.json +1 -1
- package/build/tries/meta.json +1 -1
- package/build/tries/private.complete.json +1 -1
- package/lib/parseDomain.js +18 -36
- package/lib/trie/characters.js +16 -0
- package/lib/trie/lookUp.js +33 -0
- package/lib/trie/nodes.js +40 -0
- package/lib/trie/parse.js +64 -0
- package/lib/{tries → trie}/parsePubSuffixList.js +0 -0
- package/lib/{tries → trie}/serializeTrie.js +6 -6
- package/lists/icann.complete.js +2 -2
- package/lists/icann.light.js +2 -2
- package/lists/private.complete.js +2 -2
- package/package.json +1 -1
- package/scripts/build-tries.js +3 -3
- package/test/parseDomain.test.js +124 -1
- package/test/snapshots.test.js +2 -23
- package/test/trie/lookUp.test.js +99 -0
- package/test/trie/nodes.test.js +22 -0
- package/test/trie/parse.test.js +143 -0
- package/test/{tries → trie}/parsePubSuffixList.test.js +1 -1
- package/test/{tries → trie}/serializeTrie.test.js +1 -1
- package/build/.DS_Store +0 -0
- package/lib/tries/lookUp.js +0 -40
- package/lib/tries/parseTrie.js +0 -62
- package/lib/tries/separators.js +0 -12
- package/test/tries/lookUp.test.js +0 -116
- package/test/tries/parseTrie.test.js +0 -162
package/lib/parseDomain.js
CHANGED
|
@@ -6,53 +6,35 @@ var privateTrie = require("../lists/private.complete");
|
|
|
6
6
|
|
|
7
7
|
var normalize = require("./normalize.js");
|
|
8
8
|
|
|
9
|
-
var lookUp = require("./
|
|
9
|
+
var lookUp = require("./trie/lookUp"); // eslint-disable-next-line
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
var urlParts = /^(:?\/\/|https?:\/\/)?([^/]*@)?(.+?)(:\d{2,5})?([/?].*)?$/; // 1 = protocol, 2 = auth, 3 =
|
|
12
|
+
var urlParts = /^(:?\/\/|https?:\/\/)?([^/]*@)?(.+?)(:\d{2,5})?([/?].*)?$/; // 1 = protocol, 2 = auth, 3 = hostname, 4 = port, 5 = path
|
|
13
13
|
|
|
14
14
|
var dot = /\./g;
|
|
15
15
|
var emptyArr = [];
|
|
16
16
|
|
|
17
|
-
function matchTld(
|
|
17
|
+
function matchTld(hostname, options) {
|
|
18
18
|
// for potentially unrecognized tlds, try matching against custom tlds
|
|
19
19
|
if (options.customTlds) {
|
|
20
20
|
// try matching against a built regexp of custom tlds
|
|
21
|
-
var tld =
|
|
21
|
+
var tld = hostname.match(options.customTlds);
|
|
22
22
|
|
|
23
23
|
if (tld !== null) {
|
|
24
24
|
return tld[0];
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
var
|
|
29
|
-
var
|
|
30
|
-
var
|
|
31
|
-
var _iteratorError = undefined;
|
|
28
|
+
var domains = hostname.split(".");
|
|
29
|
+
var icannTlds = lookUp(icannTrie, domains);
|
|
30
|
+
var privateTlds = options.privateTlds ? lookUp(privateTrie, domains) : emptyArr;
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
var _tld = lookUp(trie, domain);
|
|
32
|
+
if (privateTlds.length > icannTlds.length) {
|
|
33
|
+
return "." + privateTlds.join(".");
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
} catch (err) {
|
|
44
|
-
_didIteratorError = true;
|
|
45
|
-
_iteratorError = err;
|
|
46
|
-
} finally {
|
|
47
|
-
try {
|
|
48
|
-
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
|
|
49
|
-
_iterator["return"]();
|
|
50
|
-
}
|
|
51
|
-
} finally {
|
|
52
|
-
if (_didIteratorError) {
|
|
53
|
-
throw _iteratorError;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
36
|
+
if (icannTlds.length > 0) {
|
|
37
|
+
return "." + icannTlds.join(".");
|
|
56
38
|
}
|
|
57
39
|
|
|
58
40
|
return null;
|
|
@@ -80,7 +62,7 @@ function parseDomain(url, options) {
|
|
|
80
62
|
var normalizedUrl = normalize.url(url);
|
|
81
63
|
var tld = null;
|
|
82
64
|
var urlSplit;
|
|
83
|
-
var
|
|
65
|
+
var hostname;
|
|
84
66
|
|
|
85
67
|
if (!normalizedUrl) {
|
|
86
68
|
return null;
|
|
@@ -93,27 +75,27 @@ function parseDomain(url, options) {
|
|
|
93
75
|
return null;
|
|
94
76
|
}
|
|
95
77
|
|
|
96
|
-
|
|
78
|
+
hostname = urlSplit[3]; // domain will now be something like sub.domain.example.com
|
|
97
79
|
|
|
98
|
-
tld = matchTld(
|
|
80
|
+
tld = matchTld(hostname, normalizedOptions);
|
|
99
81
|
|
|
100
82
|
if (tld === null) {
|
|
101
83
|
return null;
|
|
102
84
|
} // remove tld and split by dot
|
|
103
85
|
|
|
104
86
|
|
|
105
|
-
urlSplit =
|
|
87
|
+
urlSplit = hostname.slice(0, -tld.length).split(dot);
|
|
106
88
|
|
|
107
89
|
if (tld.charAt(0) === ".") {
|
|
108
90
|
// removes the remaining dot, if present (added to handle localhost)
|
|
109
91
|
tld = tld.slice(1);
|
|
110
92
|
}
|
|
111
93
|
|
|
112
|
-
|
|
94
|
+
hostname = urlSplit.pop();
|
|
113
95
|
var subdomain = urlSplit.join(".");
|
|
114
96
|
return {
|
|
115
97
|
tld: tld,
|
|
116
|
-
domain:
|
|
98
|
+
domain: hostname,
|
|
117
99
|
subdomain: subdomain
|
|
118
100
|
};
|
|
119
101
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
UP: "<",
|
|
5
|
+
// one level up
|
|
6
|
+
SAME: ",",
|
|
7
|
+
// same level
|
|
8
|
+
DOWN: ">",
|
|
9
|
+
// one level down
|
|
10
|
+
RESET: "|",
|
|
11
|
+
// reset level index and start new
|
|
12
|
+
WILDCARD: "*",
|
|
13
|
+
// as defined by publicsuffix.org
|
|
14
|
+
EXCEPTION: "!" // as defined by publicsuffix.org
|
|
15
|
+
|
|
16
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var characters = require("./characters.js");
|
|
4
|
+
|
|
5
|
+
function lookUp(trie, domains) {
|
|
6
|
+
var domainsToCheck = domains.slice();
|
|
7
|
+
var topLevel = [];
|
|
8
|
+
var node = trie;
|
|
9
|
+
|
|
10
|
+
while (domainsToCheck.length) {
|
|
11
|
+
var domain = domainsToCheck.pop();
|
|
12
|
+
|
|
13
|
+
if (node.children.has(characters.WILDCARD)) {
|
|
14
|
+
if (node.children.has(characters.EXCEPTION + domain)) {
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
node = node.children.get(characters.WILDCARD);
|
|
19
|
+
} else {
|
|
20
|
+
if (node.children.has(domain) === false) {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
node = node.children.get(domain);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
topLevel.unshift(domain);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return topLevel;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = lookUp;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function createNode() {
|
|
4
|
+
var domain = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
|
|
5
|
+
return {
|
|
6
|
+
domain: domain,
|
|
7
|
+
children: new Map(),
|
|
8
|
+
parent: null
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createOrGetChild(parent, domain) {
|
|
13
|
+
var child = parent.children.get(domain);
|
|
14
|
+
|
|
15
|
+
if (child === undefined) {
|
|
16
|
+
child = createNode(domain);
|
|
17
|
+
adoptChild(parent, child);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return child;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function adoptChild(parent, child) {
|
|
24
|
+
if (typeof child.domain !== "string") {
|
|
25
|
+
throw new Error("Cannot adopt child: child.domain must be a string");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (parent.children.has(child.domain)) {
|
|
29
|
+
throw new Error("Cannot adopt child: parent has already a child with the domain '".concat(child.domain, "'"));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
parent.children.set(child.domain, child);
|
|
33
|
+
child.parent = parent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
createNode: createNode,
|
|
38
|
+
createOrGetChild: createOrGetChild,
|
|
39
|
+
adoptChild: adoptChild
|
|
40
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var characters = require("./characters");
|
|
4
|
+
|
|
5
|
+
var _require = require("./nodes"),
|
|
6
|
+
createNode = _require.createNode,
|
|
7
|
+
createOrGetChild = _require.createOrGetChild; // Parsing is complex... :)
|
|
8
|
+
// eslint-disable-next-line complexity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
function parse(serialized) {
|
|
12
|
+
var rootNode = createNode();
|
|
13
|
+
var domain = "";
|
|
14
|
+
var parentNode = rootNode;
|
|
15
|
+
var node;
|
|
16
|
+
|
|
17
|
+
function addDomain() {
|
|
18
|
+
node = createOrGetChild(parentNode, domain);
|
|
19
|
+
domain = "";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (var i = 0; i < serialized.length; i++) {
|
|
23
|
+
var _char = serialized.charAt(i);
|
|
24
|
+
|
|
25
|
+
switch (_char) {
|
|
26
|
+
case characters.SAME:
|
|
27
|
+
{
|
|
28
|
+
addDomain();
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
case characters.DOWN:
|
|
33
|
+
{
|
|
34
|
+
addDomain();
|
|
35
|
+
parentNode = node;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
case characters.RESET:
|
|
40
|
+
{
|
|
41
|
+
addDomain();
|
|
42
|
+
parentNode = rootNode;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
case characters.UP:
|
|
47
|
+
{
|
|
48
|
+
addDomain();
|
|
49
|
+
parentNode = parentNode.parent;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
domain += _char;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (domain !== "") {
|
|
58
|
+
addDomain();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return rootNode;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = parse;
|
|
File without changes
|
|
@@ -4,7 +4,7 @@ var _LINE_FILTERS;
|
|
|
4
4
|
|
|
5
5
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var characters = require("./characters");
|
|
8
8
|
|
|
9
9
|
var TYPE_COMPLETE = "complete";
|
|
10
10
|
var TYPE_LIGHT = "light";
|
|
@@ -54,14 +54,14 @@ function lineToString(line, i, arr) {
|
|
|
54
54
|
|
|
55
55
|
if (indexOfDifference === 0) {
|
|
56
56
|
// line and prevLine are completely different
|
|
57
|
-
separatorFromPrev =
|
|
57
|
+
separatorFromPrev = characters.RESET;
|
|
58
58
|
} else if (indexOfDifference === prevLine.length - 1) {
|
|
59
59
|
// only the last part of line and prevLine are different
|
|
60
|
-
separatorFromPrev =
|
|
60
|
+
separatorFromPrev = characters.SAME;
|
|
61
61
|
} else if (indexOfDifference > prevLine.length - 1) {
|
|
62
62
|
// we don't need to go up the hierarchy first because prevLine is part of line
|
|
63
63
|
// so let's just start with an initial down separator
|
|
64
|
-
separatorFromPrev =
|
|
64
|
+
separatorFromPrev = characters.DOWN;
|
|
65
65
|
} else {
|
|
66
66
|
// line and prevLine are different, but share a common root at indexOfDifference - 1
|
|
67
67
|
// we now need to go up the hierarchy to the common root
|
|
@@ -76,11 +76,11 @@ function lineToString(line, i, arr) {
|
|
|
76
76
|
throw new Error("Cannot serialize trie: The public suffix list contains unexpected rules.");
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
separatorFromPrev = new Array(levelsUp).fill(
|
|
79
|
+
separatorFromPrev = new Array(levelsUp).fill(characters.UP).join("");
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
return separatorFromPrev + line.slice(indexOfDifference).join(
|
|
83
|
+
return separatorFromPrev + line.slice(indexOfDifference).join(characters.DOWN);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
function serializeTrie(parsedList, type) {
|
package/lists/icann.complete.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const parse = require("../lib/trie/parse");
|
|
4
4
|
|
|
5
|
-
module.exports =
|
|
5
|
+
module.exports = parse(require("../build/tries/icann.complete.json").trie);
|
package/lists/icann.light.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const parse = require("../lib/trie/parse");
|
|
4
4
|
|
|
5
|
-
module.exports =
|
|
5
|
+
module.exports = parse(require("../build/tries/icann.light.json").trie);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const parse = require("../lib/trie/parse");
|
|
4
4
|
|
|
5
|
-
module.exports =
|
|
5
|
+
module.exports = parse(require("../build/tries/private.complete.json").trie);
|
package/package.json
CHANGED
package/scripts/build-tries.js
CHANGED
|
@@ -12,7 +12,7 @@ const got = require("got");
|
|
|
12
12
|
const mkdirp = require("mkdirp");
|
|
13
13
|
|
|
14
14
|
try {
|
|
15
|
-
require.resolve("../lib/
|
|
15
|
+
require.resolve("../lib/trie/parsePubSuffixList");
|
|
16
16
|
} catch (error) {
|
|
17
17
|
// This conditions occurs when the CI system or a developer checks out the repo for the first time.
|
|
18
18
|
// It happens because the postinstall hook kicks in *before* the lib is built.
|
|
@@ -20,8 +20,8 @@ try {
|
|
|
20
20
|
console.error("Lib does not exist yet, skipping build-tries step.");
|
|
21
21
|
process.exit(0); // eslint-disable-line no-process-exit
|
|
22
22
|
}
|
|
23
|
-
const parsePubSuffixList = require("../lib/
|
|
24
|
-
const serializeTrie = require("../lib/
|
|
23
|
+
const parsePubSuffixList = require("../lib/trie/parsePubSuffixList");
|
|
24
|
+
const serializeTrie = require("../lib/trie/serializeTrie");
|
|
25
25
|
|
|
26
26
|
const PUBLIC_SUFFIX_URL = "https://publicsuffix.org/list/public_suffix_list.dat";
|
|
27
27
|
const rootPath = path.resolve(__dirname, "..");
|
package/test/parseDomain.test.js
CHANGED
|
@@ -219,7 +219,7 @@ describe("parseDomain(url)", () => {
|
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
// See https://github.com/peerigon/parse-domain/issues/67
|
|
222
|
-
it
|
|
222
|
+
it("should parse gouv.fr as tld", () => {
|
|
223
223
|
expect(parseDomain("dev.classea12.beta.gouv.fr", {privateTlds: true})).to.eql({
|
|
224
224
|
tld: "gouv.fr",
|
|
225
225
|
domain: "beta",
|
|
@@ -227,4 +227,127 @@ describe("parseDomain(url)", () => {
|
|
|
227
227
|
});
|
|
228
228
|
});
|
|
229
229
|
});
|
|
230
|
+
|
|
231
|
+
describe("official test suite", () => {
|
|
232
|
+
it("passes all inputs", () => {
|
|
233
|
+
function checkPublicSuffix(input, expectedTld) {
|
|
234
|
+
console.log(input);
|
|
235
|
+
const result = parseDomain(input, {privateTlds: true});
|
|
236
|
+
|
|
237
|
+
if (expectedTld === null) {
|
|
238
|
+
// When the expectedTld is null it means that the domain
|
|
239
|
+
// should not be registerable.
|
|
240
|
+
// Our module will return an empty string for the domain
|
|
241
|
+
// in that case. The tld should match the input.
|
|
242
|
+
expect(result.tld).to.equal(input.toLowerCase());
|
|
243
|
+
expect(result.domain).to.equal("");
|
|
244
|
+
} else {
|
|
245
|
+
expect(result.domain + "." + result.tld).to.equal(expectedTld);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Any copyright is dedicated to the Public Domain.
|
|
250
|
+
// https://creativecommons.org/publicdomain/zero/1.0/
|
|
251
|
+
|
|
252
|
+
// null input.
|
|
253
|
+
// checkPublicSuffix(null, null);
|
|
254
|
+
// Mixed case.
|
|
255
|
+
checkPublicSuffix("COM", null);
|
|
256
|
+
checkPublicSuffix("example.COM", "example.com");
|
|
257
|
+
checkPublicSuffix("WwW.example.COM", "example.com");
|
|
258
|
+
// Leading dot.
|
|
259
|
+
// TODO: Make this test work
|
|
260
|
+
// checkPublicSuffix(".com", null);
|
|
261
|
+
// checkPublicSuffix(".example", null);
|
|
262
|
+
// checkPublicSuffix(".example.com", null);
|
|
263
|
+
// checkPublicSuffix(".example.example", null);
|
|
264
|
+
// Unlisted TLD.
|
|
265
|
+
// This is a strange test case. Why should 'example.example' be registerable when 'example' is unknown?
|
|
266
|
+
// checkPublicSuffix("example", null);
|
|
267
|
+
// checkPublicSuffix("example.example", "example.example");
|
|
268
|
+
// checkPublicSuffix("b.example.example", "example.example");
|
|
269
|
+
// checkPublicSuffix("a.b.example.example", "example.example");
|
|
270
|
+
// Listed, but non-Internet, TLD.
|
|
271
|
+
// checkPublicSuffix('local', null);
|
|
272
|
+
// checkPublicSuffix('example.local', null);
|
|
273
|
+
// checkPublicSuffix('b.example.local', null);
|
|
274
|
+
// checkPublicSuffix('a.b.example.local', null);
|
|
275
|
+
// TLD with only 1 rule.
|
|
276
|
+
checkPublicSuffix("biz", null);
|
|
277
|
+
checkPublicSuffix("domain.biz", "domain.biz");
|
|
278
|
+
checkPublicSuffix("b.domain.biz", "domain.biz");
|
|
279
|
+
checkPublicSuffix("a.b.domain.biz", "domain.biz");
|
|
280
|
+
// TLD with some 2-level rules.
|
|
281
|
+
checkPublicSuffix("com", null);
|
|
282
|
+
checkPublicSuffix("example.com", "example.com");
|
|
283
|
+
checkPublicSuffix("b.example.com", "example.com");
|
|
284
|
+
checkPublicSuffix("a.b.example.com", "example.com");
|
|
285
|
+
checkPublicSuffix("uk.com", null);
|
|
286
|
+
checkPublicSuffix("example.uk.com", "example.uk.com");
|
|
287
|
+
checkPublicSuffix("b.example.uk.com", "example.uk.com");
|
|
288
|
+
checkPublicSuffix("a.b.example.uk.com", "example.uk.com");
|
|
289
|
+
checkPublicSuffix("test.ac", "test.ac");
|
|
290
|
+
// TLD with only 1 (wildcard) rule.
|
|
291
|
+
checkPublicSuffix("mm", null);
|
|
292
|
+
checkPublicSuffix("c.mm", null);
|
|
293
|
+
checkPublicSuffix("b.c.mm", "b.c.mm");
|
|
294
|
+
checkPublicSuffix("a.b.c.mm", "b.c.mm");
|
|
295
|
+
// More complex TLD.
|
|
296
|
+
checkPublicSuffix("jp", null);
|
|
297
|
+
checkPublicSuffix("test.jp", "test.jp");
|
|
298
|
+
checkPublicSuffix("www.test.jp", "test.jp");
|
|
299
|
+
checkPublicSuffix("ac.jp", null);
|
|
300
|
+
checkPublicSuffix("test.ac.jp", "test.ac.jp");
|
|
301
|
+
checkPublicSuffix("www.test.ac.jp", "test.ac.jp");
|
|
302
|
+
checkPublicSuffix("kyoto.jp", null);
|
|
303
|
+
checkPublicSuffix("test.kyoto.jp", "test.kyoto.jp");
|
|
304
|
+
checkPublicSuffix("ide.kyoto.jp", null);
|
|
305
|
+
checkPublicSuffix("b.ide.kyoto.jp", "b.ide.kyoto.jp");
|
|
306
|
+
checkPublicSuffix("a.b.ide.kyoto.jp", "b.ide.kyoto.jp");
|
|
307
|
+
checkPublicSuffix("c.kobe.jp", null);
|
|
308
|
+
checkPublicSuffix("b.c.kobe.jp", "b.c.kobe.jp");
|
|
309
|
+
checkPublicSuffix("a.b.c.kobe.jp", "b.c.kobe.jp");
|
|
310
|
+
checkPublicSuffix("city.kobe.jp", "city.kobe.jp");
|
|
311
|
+
checkPublicSuffix("www.city.kobe.jp", "city.kobe.jp");
|
|
312
|
+
// TLD with a wildcard rule and exceptions.
|
|
313
|
+
checkPublicSuffix("ck", null);
|
|
314
|
+
checkPublicSuffix("test.ck", null);
|
|
315
|
+
checkPublicSuffix("b.test.ck", "b.test.ck");
|
|
316
|
+
checkPublicSuffix("a.b.test.ck", "b.test.ck");
|
|
317
|
+
checkPublicSuffix("www.ck", "www.ck");
|
|
318
|
+
checkPublicSuffix("www.www.ck", "www.ck");
|
|
319
|
+
// US K12.
|
|
320
|
+
checkPublicSuffix("us", null);
|
|
321
|
+
checkPublicSuffix("test.us", "test.us");
|
|
322
|
+
checkPublicSuffix("www.test.us", "test.us");
|
|
323
|
+
checkPublicSuffix("ak.us", null);
|
|
324
|
+
checkPublicSuffix("test.ak.us", "test.ak.us");
|
|
325
|
+
checkPublicSuffix("www.test.ak.us", "test.ak.us");
|
|
326
|
+
checkPublicSuffix("k12.ak.us", null);
|
|
327
|
+
checkPublicSuffix("test.k12.ak.us", "test.k12.ak.us");
|
|
328
|
+
checkPublicSuffix("www.test.k12.ak.us", "test.k12.ak.us");
|
|
329
|
+
// IDN labels.
|
|
330
|
+
checkPublicSuffix("食狮.com.cn", "食狮.com.cn");
|
|
331
|
+
checkPublicSuffix("食狮.公司.cn", "食狮.公司.cn");
|
|
332
|
+
checkPublicSuffix("www.食狮.公司.cn", "食狮.公司.cn");
|
|
333
|
+
checkPublicSuffix("shishi.公司.cn", "shishi.公司.cn");
|
|
334
|
+
checkPublicSuffix("公司.cn", null);
|
|
335
|
+
checkPublicSuffix("食狮.中国", "食狮.中国");
|
|
336
|
+
checkPublicSuffix("www.食狮.中国", "食狮.中国");
|
|
337
|
+
checkPublicSuffix("shishi.中国", "shishi.中国");
|
|
338
|
+
checkPublicSuffix("中国", null);
|
|
339
|
+
// Same as above, but punycoded.
|
|
340
|
+
// Punycode is currently not supported
|
|
341
|
+
// TODO: Make these tests work
|
|
342
|
+
// checkPublicSuffix("xn--85x722f.com.cn", "xn--85x722f.com.cn");
|
|
343
|
+
// checkPublicSuffix("xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn");
|
|
344
|
+
// checkPublicSuffix("www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn");
|
|
345
|
+
// checkPublicSuffix("shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn");
|
|
346
|
+
// checkPublicSuffix("xn--55qx5d.cn", null);
|
|
347
|
+
// checkPublicSuffix("xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s");
|
|
348
|
+
// checkPublicSuffix("www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s");
|
|
349
|
+
// checkPublicSuffix("shishi.xn--fiqs8s", "shishi.xn--fiqs8s");
|
|
350
|
+
// checkPublicSuffix("xn--fiqs8s", null);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
230
353
|
});
|
package/test/snapshots.test.js
CHANGED
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const chai = require("chai");
|
|
6
|
-
const parsePubSuffixList = require("../lib/
|
|
7
|
-
const serializeTrie = require("../lib/
|
|
8
|
-
const parseTrie = require("../lib/tries/parseTrie");
|
|
9
|
-
const lookUp = require("../lib/tries/lookUp");
|
|
6
|
+
const parsePubSuffixList = require("../lib/trie/parsePubSuffixList");
|
|
7
|
+
const serializeTrie = require("../lib/trie/serializeTrie");
|
|
10
8
|
|
|
11
9
|
const expect = chai.expect;
|
|
12
10
|
const TEST_SNAPSHOT = true;
|
|
@@ -36,23 +34,4 @@ describe("snapshots", () => {
|
|
|
36
34
|
fs.writeFileSync(pathToSerializeTrieSnapshot, JSON.stringify(serializedTrie));
|
|
37
35
|
});
|
|
38
36
|
});
|
|
39
|
-
describe("parseTrie() and lookUp() calling lookUp() with the result from parseTrie(snapshot) and hostname", () => {
|
|
40
|
-
const serializedTrie = JSON.parse(fs.readFileSync(pathToSerializeTrieSnapshot, "utf8"));
|
|
41
|
-
const parsedTrie = parseTrie(serializedTrie);
|
|
42
|
-
|
|
43
|
-
[
|
|
44
|
-
["example.com", "com"],
|
|
45
|
-
["example.a.com", "com"],
|
|
46
|
-
["example.uk", "uk"],
|
|
47
|
-
["example.co.uk", "co.uk"],
|
|
48
|
-
["example.ab.uk", "uk"],
|
|
49
|
-
].forEach(testArgs => {
|
|
50
|
-
const hostname = testArgs[0];
|
|
51
|
-
const expectedResult = testArgs[1];
|
|
52
|
-
|
|
53
|
-
it(`'${hostname}' returns ${expectedResult}`, () => {
|
|
54
|
-
expect(lookUp(parsedTrie, hostname)).to.equal(expectedResult);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
37
|
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const chai = require("chai");
|
|
4
|
+
const lookUp = require("../../lib/trie/lookUp");
|
|
5
|
+
const characters = require("../../lib/trie/characters");
|
|
6
|
+
const {createNode, adoptChild} = require("../../lib/trie/nodes");
|
|
7
|
+
|
|
8
|
+
const expect = chai.expect;
|
|
9
|
+
|
|
10
|
+
function createOneLevelDeepTrie() {
|
|
11
|
+
const root = createNode();
|
|
12
|
+
const com = createNode("com");
|
|
13
|
+
|
|
14
|
+
adoptChild(root, com);
|
|
15
|
+
|
|
16
|
+
return root;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createTwoLevelDeepTrie() {
|
|
20
|
+
const root = createNode();
|
|
21
|
+
const uk = createNode("uk");
|
|
22
|
+
const co = createNode("co");
|
|
23
|
+
|
|
24
|
+
adoptChild(root, uk);
|
|
25
|
+
adoptChild(uk, co);
|
|
26
|
+
|
|
27
|
+
return root;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createTrieWithWildcardAndException() {
|
|
31
|
+
const root = createNode();
|
|
32
|
+
const wildcard = createNode(characters.WILDCARD);
|
|
33
|
+
const exception = createNode(characters.EXCEPTION + "exception");
|
|
34
|
+
|
|
35
|
+
adoptChild(root, wildcard);
|
|
36
|
+
adoptChild(root, exception);
|
|
37
|
+
|
|
38
|
+
return root;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("lookUp()", () => {
|
|
42
|
+
describe("with an empty trie", () => {
|
|
43
|
+
it("returns an empty array", () => {
|
|
44
|
+
const root = createNode();
|
|
45
|
+
|
|
46
|
+
expect(lookUp(root, ["com"])).to.eql([]);
|
|
47
|
+
expect(lookUp(root, ["example", "com"])).to.eql([]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("with a one level deep trie", () => {
|
|
51
|
+
it("returns the known TLD as array", () => {
|
|
52
|
+
const root = createOneLevelDeepTrie();
|
|
53
|
+
|
|
54
|
+
expect(lookUp(root, ["com"])).to.eql(["com"]);
|
|
55
|
+
expect(lookUp(root, ["example", "com"])).to.eql(["com"]);
|
|
56
|
+
expect(lookUp(root, ["www", "example", "com"])).to.eql(["com"]);
|
|
57
|
+
});
|
|
58
|
+
it("returns an empty array if there is no known TLD", () => {
|
|
59
|
+
const root = createOneLevelDeepTrie();
|
|
60
|
+
|
|
61
|
+
expect(lookUp(root, ["de"])).to.eql([]);
|
|
62
|
+
expect(lookUp(root, ["example", "de"])).to.eql([]);
|
|
63
|
+
expect(lookUp(root, ["www", "example", "de"])).to.eql([]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe("with a two level deep trie", () => {
|
|
67
|
+
it("returns the known TLD", () => {
|
|
68
|
+
const root = createTwoLevelDeepTrie();
|
|
69
|
+
|
|
70
|
+
expect(lookUp(root, ["uk"])).to.eql(["uk"]);
|
|
71
|
+
expect(lookUp(root, ["example", "uk"])).to.eql(["uk"]);
|
|
72
|
+
expect(lookUp(root, ["co", "uk"])).to.eql(["co", "uk"]);
|
|
73
|
+
expect(lookUp(root, ["example", "co", "uk"])).to.eql(["co", "uk"]);
|
|
74
|
+
expect(lookUp(root, ["www", "example", "co", "uk"])).to.eql(["co", "uk"]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns an empty array if there is no known TLD", () => {
|
|
78
|
+
const root = createTwoLevelDeepTrie();
|
|
79
|
+
|
|
80
|
+
expect(lookUp(root, ["de"])).to.eql([]);
|
|
81
|
+
expect(lookUp(root, ["example", "de"])).to.eql([]);
|
|
82
|
+
expect(lookUp(root, ["www", "example", "de"])).to.eql([]);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("with a wildcard and exceptions", () => {
|
|
86
|
+
it("does not accept the exception as TLD", () => {
|
|
87
|
+
const root = createTrieWithWildcardAndException();
|
|
88
|
+
|
|
89
|
+
expect(lookUp(root, ["exception"])).to.eql([]);
|
|
90
|
+
expect(lookUp(root, ["www", "exception"])).to.eql([]);
|
|
91
|
+
});
|
|
92
|
+
it("accepts all domain names as TLD in place of the wildcard", () => {
|
|
93
|
+
const root = createTrieWithWildcardAndException();
|
|
94
|
+
|
|
95
|
+
expect(lookUp(root, ["anything"])).to.eql(["anything"]);
|
|
96
|
+
expect(lookUp(root, ["www", "anything"])).to.eql(["anything"]);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const chai = require("chai");
|
|
4
|
+
const {createNode, adoptChild} = require("../../lib/trie/nodes");
|
|
5
|
+
|
|
6
|
+
const expect = chai.expect;
|
|
7
|
+
|
|
8
|
+
describe("adoptChild()", () => {
|
|
9
|
+
it("throws an error if the child has no domain", () => {
|
|
10
|
+
expect(() => {
|
|
11
|
+
adoptChild(createNode(), createNode());
|
|
12
|
+
}).to.throw("Cannot adopt child: child.domain must be a string");
|
|
13
|
+
});
|
|
14
|
+
it("throws an error if the parent has already a child with that domain", () => {
|
|
15
|
+
expect(() => {
|
|
16
|
+
const parent = createNode();
|
|
17
|
+
|
|
18
|
+
adoptChild(parent, createNode("some-domain"));
|
|
19
|
+
adoptChild(parent, createNode("some-domain"));
|
|
20
|
+
}).to.throw("Cannot adopt child: parent has already a child with the domain 'some-domain'");
|
|
21
|
+
});
|
|
22
|
+
});
|