parse-domain 2.2.0 → 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 +38 -0
- package/README.md +1 -1
- package/build/tries/icann.complete.json +1 -1
- package/build/tries/icann.light.json +1 -1
- package/build/tries/meta.json +1 -0
- package/build/tries/private.complete.json +1 -1
- package/lib/parseDomain.js +24 -38
- 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 +6 -4
- package/scripts/build-tries.js +14 -6
- package/src/index.d.ts +18 -0
- package/test/parseDomain.test.js +152 -10
- 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/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,36 +62,40 @@ 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;
|
|
87
69
|
}
|
|
88
70
|
|
|
89
|
-
var normalizedOptions = normalize.options(options);
|
|
71
|
+
var normalizedOptions = normalize.options(options);
|
|
72
|
+
urlSplit = normalizedUrl.match(urlParts); // urlSplit is null if the url contains certain characters like '\n', '\r'.
|
|
73
|
+
|
|
74
|
+
if (urlSplit === null) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
90
77
|
|
|
91
|
-
|
|
92
|
-
domain = urlSplit[3]; // domain will now be something like sub.domain.example.com
|
|
78
|
+
hostname = urlSplit[3]; // domain will now be something like sub.domain.example.com
|
|
93
79
|
|
|
94
|
-
tld = matchTld(
|
|
80
|
+
tld = matchTld(hostname, normalizedOptions);
|
|
95
81
|
|
|
96
82
|
if (tld === null) {
|
|
97
83
|
return null;
|
|
98
84
|
} // remove tld and split by dot
|
|
99
85
|
|
|
100
86
|
|
|
101
|
-
urlSplit =
|
|
87
|
+
urlSplit = hostname.slice(0, -tld.length).split(dot);
|
|
102
88
|
|
|
103
89
|
if (tld.charAt(0) === ".") {
|
|
104
90
|
// removes the remaining dot, if present (added to handle localhost)
|
|
105
91
|
tld = tld.slice(1);
|
|
106
92
|
}
|
|
107
93
|
|
|
108
|
-
|
|
94
|
+
hostname = urlSplit.pop();
|
|
109
95
|
var subdomain = urlSplit.join(".");
|
|
110
96
|
return {
|
|
111
97
|
tld: tld,
|
|
112
|
-
domain:
|
|
98
|
+
domain: hostname,
|
|
113
99
|
subdomain: subdomain
|
|
114
100
|
};
|
|
115
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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parse-domain",
|
|
3
|
-
"version": "2.2
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Splits an url into sub-domain, domain and effective top-level-domain",
|
|
5
5
|
"main": "./lib/parseDomain.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "run-s build:lib build:tries",
|
|
8
9
|
"build:lib": "run-s build:lib:clean build:lib:babel",
|
|
@@ -39,7 +40,8 @@
|
|
|
39
40
|
"chai": "^4.2.0",
|
|
40
41
|
"got": "^8.3.2",
|
|
41
42
|
"mkdirp": "^0.5.1",
|
|
42
|
-
"mocha": "^6.1.4"
|
|
43
|
+
"mocha": "^6.1.4",
|
|
44
|
+
"npm-run-all": "^4.1.5"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@babel/cli": "^7.4.4",
|
|
@@ -49,7 +51,6 @@
|
|
|
49
51
|
"eslint-config-peerigon": "^17.1.0",
|
|
50
52
|
"eslint-plugin-import": "^2.17.2",
|
|
51
53
|
"eslint-plugin-jsdoc": "^4.8.4",
|
|
52
|
-
"npm-run-all": "^4.1.5",
|
|
53
54
|
"nyc": "^14.1.1",
|
|
54
55
|
"rimraf": "^2.6.3",
|
|
55
56
|
"standard-version": "^6.0.1",
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
"scripts",
|
|
61
62
|
"lists",
|
|
62
63
|
"test",
|
|
63
|
-
"build"
|
|
64
|
+
"build",
|
|
65
|
+
"src/index.d.ts"
|
|
64
66
|
]
|
|
65
67
|
}
|
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,12 +20,13 @@ 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, "..");
|
|
28
28
|
const triesPath = path.resolve(rootPath, "build", "tries");
|
|
29
|
+
const metaJsonFilename = path.resolve(triesPath, "meta.json");
|
|
29
30
|
const tries = [
|
|
30
31
|
{
|
|
31
32
|
listName: "icann",
|
|
@@ -59,7 +60,6 @@ got(PUBLIC_SUFFIX_URL, {timeout: 60 * 1000})
|
|
|
59
60
|
return {
|
|
60
61
|
path: path.resolve(triesPath, trie.filename),
|
|
61
62
|
content: JSON.stringify({
|
|
62
|
-
updatedAt: new Date().toISOString(),
|
|
63
63
|
trie: serializeTrie(parsedList, trie.type),
|
|
64
64
|
}),
|
|
65
65
|
};
|
|
@@ -80,14 +80,22 @@ got(PUBLIC_SUFFIX_URL, {timeout: 60 * 1000})
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
process.stderr.write("ok" + os.EOL);
|
|
83
|
+
|
|
84
|
+
fs.writeFileSync(
|
|
85
|
+
metaJsonFilename,
|
|
86
|
+
JSON.stringify({
|
|
87
|
+
updatedAt: new Date().toISOString(),
|
|
88
|
+
}),
|
|
89
|
+
"utf8"
|
|
90
|
+
);
|
|
83
91
|
})
|
|
84
92
|
.catch(err => {
|
|
85
93
|
console.error("");
|
|
86
94
|
console.error(`Could not update list of known top-level domains for parse-domain because of "${err.message}"`);
|
|
87
95
|
|
|
88
|
-
const
|
|
96
|
+
const metaJson = JSON.parse(fs.readFileSync(metaJsonFilename, "utf8"));
|
|
89
97
|
|
|
90
|
-
console.error("Using possibly outdated prebuilt list from " + new Date(
|
|
98
|
+
console.error("Using possibly outdated prebuilt list from " + new Date(metaJson.updatedAt).toDateString());
|
|
91
99
|
|
|
92
100
|
// We can recover using the (possibly outdated) prebuilt list, hence exit code 0
|
|
93
101
|
process.exit(0); // eslint-disable-line no-process-exit
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export as namespace parseDomain;
|
|
2
|
+
|
|
3
|
+
declare namespace parseDomain {
|
|
4
|
+
interface ParseOptions {
|
|
5
|
+
customTlds?: RegExp | Array<string>;
|
|
6
|
+
privateTlds?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ParsedDomain {
|
|
10
|
+
domain: string;
|
|
11
|
+
subdomain: string;
|
|
12
|
+
tld: string;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare function parseDomain(url: string, options?: parseDomain.ParseOptions): parseDomain.ParsedDomain | null;
|
|
17
|
+
|
|
18
|
+
export = parseDomain;
|
package/test/parseDomain.test.js
CHANGED
|
@@ -141,9 +141,17 @@ describe("parseDomain(url)", () => {
|
|
|
141
141
|
it("should return null if the given value is not a string", () => {
|
|
142
142
|
expect(parseDomain(undefined)).to.equal(null);
|
|
143
143
|
expect(parseDomain({})).to.equal(null);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should return null if the given string is not a valid URL", () => {
|
|
147
|
+
expect(parseDomain("\xa0")).to.equal(null);
|
|
144
148
|
expect(parseDomain("")).to.equal(null);
|
|
145
149
|
expect(parseDomain(" ")).to.equal(null);
|
|
146
|
-
expect(parseDomain("\
|
|
150
|
+
expect(parseDomain("http://hell.d\ne.ibm.com")).to.equal(null);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should return null if the given is an empty string with a space character", () => {
|
|
154
|
+
expect(parseDomain(" ")).to.equal(null);
|
|
147
155
|
});
|
|
148
156
|
|
|
149
157
|
it("should work with domains that could match multiple tlds", () => {
|
|
@@ -172,15 +180,6 @@ describe("parseDomain(url)", () => {
|
|
|
172
180
|
});
|
|
173
181
|
});
|
|
174
182
|
|
|
175
|
-
// See https://github.com/peerigon/parse-domain/pull/65
|
|
176
|
-
it("should parse police.uk as tld", () => {
|
|
177
|
-
expect(parseDomain("example.police.uk")).to.eql({
|
|
178
|
-
subdomain: "",
|
|
179
|
-
domain: "example",
|
|
180
|
-
tld: "police.uk",
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
183
|
it("should also work with custom top-level domains passed as regexps", () => {
|
|
185
184
|
const options = {customTlds: /(\.local|localhost)$/};
|
|
186
185
|
|
|
@@ -208,4 +207,147 @@ describe("parseDomain(url)", () => {
|
|
|
208
207
|
tld: "com",
|
|
209
208
|
});
|
|
210
209
|
});
|
|
210
|
+
|
|
211
|
+
describe("real-world use cases", () => {
|
|
212
|
+
// See https://github.com/peerigon/parse-domain/pull/65
|
|
213
|
+
it("should parse police.uk as tld", () => {
|
|
214
|
+
expect(parseDomain("example.police.uk")).to.eql({
|
|
215
|
+
subdomain: "",
|
|
216
|
+
domain: "example",
|
|
217
|
+
tld: "police.uk",
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// See https://github.com/peerigon/parse-domain/issues/67
|
|
222
|
+
it("should parse gouv.fr as tld", () => {
|
|
223
|
+
expect(parseDomain("dev.classea12.beta.gouv.fr", {privateTlds: true})).to.eql({
|
|
224
|
+
tld: "gouv.fr",
|
|
225
|
+
domain: "beta",
|
|
226
|
+
subdomain: "dev.classea12",
|
|
227
|
+
});
|
|
228
|
+
});
|
|
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
|
+
});
|
|
211
353
|
});
|