From da961f1e9478957f3866c0c6c280297387115b8b Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Thu, 2 Sep 2021 20:12:30 +0200 Subject: [PATCH] chore: Remove custom prettier config --- bin/update.js | 26 +- prettier.config.js | 3 - serialized-tries/info.json | 2 +- src/.eslintrc.js | 2 +- src/config.ts | 4 +- src/from-url.test.ts | 146 ++--- src/from-url.ts | 48 +- src/main.ts | 18 +- src/parse-domain.test.ts | 866 +++++++++++++------------- src/parse-domain.ts | 304 +++++---- src/psl/build-tries.test.ts | 104 ++-- src/psl/build-tries.ts | 96 +-- src/psl/fetch-psl.test.ts | 52 +- src/psl/fetch-psl.ts | 20 +- src/publicsuffix.test.ts | 996 +++++++++++++++--------------- src/sanitize.ts | 268 ++++---- src/scripts/update-psl-fixture.ts | 24 +- src/scripts/update-tries.ts | 80 +-- src/serialized-tries.ts | 2 +- src/smoke-test.ts | 112 ++-- src/tests/assertions/nodes.ts | 46 +- src/tests/fixtures/fixtures.ts | 16 +- src/trie/create-trie.test.ts | 110 ++-- src/trie/create-trie.ts | 18 +- src/trie/look-up.ts | 44 +- src/trie/nodes.ts | 42 +- src/trie/parse-trie.test.ts | 238 +++---- src/trie/parse-trie.ts | 96 +-- src/trie/serialize-trie.test.ts | 100 +-- src/trie/serialize-trie.ts | 54 +- src/type-util.ts | 6 +- src/update-tries.test.ts | 36 +- src/update-tries.ts | 22 +- 33 files changed, 1996 insertions(+), 2005 deletions(-) delete mode 100644 prettier.config.js diff --git a/bin/update.js b/bin/update.js index 0920f69..11e2879 100755 --- a/bin/update.js +++ b/bin/update.js @@ -2,24 +2,24 @@ "use strict"; -const {EOL} = require("os"); +const { EOL } = require("os"); (async () => { - process.argv.push( - "--", - "../../serialized-tries", - "../../../build-esm/serialized-tries", - ); + process.argv.push( + "--", + "../../serialized-tries", + "../../../build-esm/serialized-tries" + ); - await require("../build-cjs/src/scripts/update-tries.js").done; + await require("../build-cjs/src/scripts/update-tries.js").done; - process.stderr.write("Running smoke test... "); + process.stderr.write("Running smoke test... "); - require("../build-cjs/src/smoke-test.js").runSmokeTest(); + require("../build-cjs/src/smoke-test.js").runSmokeTest(); - process.stdout.write("ok" + EOL); + process.stdout.write("ok" + EOL); })().catch((error) => { - console.error(`parse-domain update failed: ${error}`); - // eslint-disable-next-line no-process-exit - process.exit(1); + console.error(`parse-domain update failed: ${error}`); + // eslint-disable-next-line no-process-exit + process.exit(1); }); diff --git a/prettier.config.js b/prettier.config.js deleted file mode 100644 index d8fc550..0000000 --- a/prettier.config.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; - -module.exports = require("eslint-config-peerigon/prettier.js"); diff --git a/serialized-tries/info.json b/serialized-tries/info.json index c5dbed0..b594f5a 100644 --- a/serialized-tries/info.json +++ b/serialized-tries/info.json @@ -1 +1 @@ -{"updatedAt": "2020-04-24T17:18:33.648Z"} +{ "updatedAt": "2020-04-24T17:18:33.648Z" } diff --git a/src/.eslintrc.js b/src/.eslintrc.js index 3387632..8b0cbba 100644 --- a/src/.eslintrc.js +++ b/src/.eslintrc.js @@ -16,4 +16,4 @@ module.exports = { project: "./tsconfig.json", sourceType: "module", }, -}; \ No newline at end of file +}; diff --git a/src/config.ts b/src/config.ts index 9f1849d..2c88144 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,8 +1,8 @@ export const PUBLIC_SUFFIX_URL = - "https://publicsuffix.org/list/public_suffix_list.dat"; + "https://publicsuffix.org/list/public_suffix_list.dat"; export const PUBLIC_SUFFIX_MARKER_ICANN_START = "// ===BEGIN ICANN DOMAINS==="; export const PUBLIC_SUFFIX_MARKER_ICANN_END = "// ===END ICANN DOMAINS==="; export const PUBLIC_SUFFIX_MARKER_PRIVATE_START = - "// ===BEGIN PRIVATE DOMAINS==="; + "// ===BEGIN PRIVATE DOMAINS==="; export const PUBLIC_SUFFIX_MARKER_PRIVATE_END = "// ===END PRIVATE DOMAINS==="; export const FETCH_PSL_EXPECTED_MIN_LENGTH = 214528; diff --git a/src/from-url.test.ts b/src/from-url.test.ts index d1db976..42d4c41 100644 --- a/src/from-url.test.ts +++ b/src/from-url.test.ts @@ -1,86 +1,86 @@ -import {fromUrl, NO_HOSTNAME} from "./from-url"; +import { fromUrl, NO_HOSTNAME } from "./from-url"; describe(fromUrl.name, () => { - test("it returns the hostname only", () => { - expect(fromUrl("https://user@www.example.com:8080/path?query")).toBe( - "www.example.com", - ); - }); + test("it returns the hostname only", () => { + expect(fromUrl("https://user@www.example.com:8080/path?query")).toBe( + "www.example.com" + ); + }); - test("it handles other protocols well", () => { - expect(fromUrl("ftp://user@www.example.com:8080/path?query")).toBe( - "www.example.com", - ); - }); + test("it handles other protocols well", () => { + expect(fromUrl("ftp://user@www.example.com:8080/path?query")).toBe( + "www.example.com" + ); + }); - test("it accepts incomplete URLs", () => { - expect(fromUrl("//user@www.example.com:8080/path?query")).toBe( - "www.example.com", - ); - expect(fromUrl("user@www.example.com:8080/path?query")).toBe( - "www.example.com", - ); - expect(fromUrl("@www.example.com:8080/path?query")).toBe("www.example.com"); - expect(fromUrl("www.example.com:8080/path?query")).toBe("www.example.com"); - expect(fromUrl("com:8080/path?query")).toBe("com"); - expect(fromUrl("com/?query")).toBe("com"); - expect(fromUrl("com?query")).toBe("com"); - }); + test("it accepts incomplete URLs", () => { + expect(fromUrl("//user@www.example.com:8080/path?query")).toBe( + "www.example.com" + ); + expect(fromUrl("user@www.example.com:8080/path?query")).toBe( + "www.example.com" + ); + expect(fromUrl("@www.example.com:8080/path?query")).toBe("www.example.com"); + expect(fromUrl("www.example.com:8080/path?query")).toBe("www.example.com"); + expect(fromUrl("com:8080/path?query")).toBe("com"); + expect(fromUrl("com/?query")).toBe("com"); + expect(fromUrl("com?query")).toBe("com"); + }); - test("it returns the root domain for URLs with just the root domain", () => { - expect(fromUrl(".:8080/path?query")).toBe("."); - expect(fromUrl("./?query")).toBe("."); - expect(fromUrl(".?query")).toBe("."); - }); + test("it returns the root domain for URLs with just the root domain", () => { + expect(fromUrl(".:8080/path?query")).toBe("."); + expect(fromUrl("./?query")).toBe("."); + expect(fromUrl(".?query")).toBe("."); + }); - test("it returns a puny-encoded hostname", () => { - expect(fromUrl("http://münchen.de")).toBe("xn--mnchen-3ya.de"); - expect(fromUrl("münchen.de")).toBe("xn--mnchen-3ya.de"); - }); + test("it returns a puny-encoded hostname", () => { + expect(fromUrl("http://münchen.de")).toBe("xn--mnchen-3ya.de"); + expect(fromUrl("münchen.de")).toBe("xn--mnchen-3ya.de"); + }); - test("it handles already puny-encoded hostnames well", () => { - expect(fromUrl("http://xn--mnchen-3ya.de")).toBe("xn--mnchen-3ya.de"); - expect(fromUrl("xn--mnchen-3ya.de")).toBe("xn--mnchen-3ya.de"); - }); + test("it handles already puny-encoded hostnames well", () => { + expect(fromUrl("http://xn--mnchen-3ya.de")).toBe("xn--mnchen-3ya.de"); + expect(fromUrl("xn--mnchen-3ya.de")).toBe("xn--mnchen-3ya.de"); + }); - test("it handles URLs with IPv4", () => { - expect(fromUrl("http://192.168.1.1/path?query")).toBe("192.168.1.1"); - expect(fromUrl("//192.168.1.1")).toBe("192.168.1.1"); - expect(fromUrl("192.168.1.1")).toBe("192.168.1.1"); - }); + test("it handles URLs with IPv4", () => { + expect(fromUrl("http://192.168.1.1/path?query")).toBe("192.168.1.1"); + expect(fromUrl("//192.168.1.1")).toBe("192.168.1.1"); + expect(fromUrl("192.168.1.1")).toBe("192.168.1.1"); + }); - test("it handles URLs with IPv6", () => { - expect(fromUrl("http://[1:2:3:4:5:6:7:8]/path?query")).toBe( - "[1:2:3:4:5:6:7:8]", - ); - expect(fromUrl("//[1:2:3:4:5:6:7:8]")).toBe("[1:2:3:4:5:6:7:8]"); - expect(fromUrl("[1:2:3:4:5:6:7:8]")).toBe("[1:2:3:4:5:6:7:8]"); - }); + test("it handles URLs with IPv6", () => { + expect(fromUrl("http://[1:2:3:4:5:6:7:8]/path?query")).toBe( + "[1:2:3:4:5:6:7:8]" + ); + expect(fromUrl("//[1:2:3:4:5:6:7:8]")).toBe("[1:2:3:4:5:6:7:8]"); + expect(fromUrl("[1:2:3:4:5:6:7:8]")).toBe("[1:2:3:4:5:6:7:8]"); + }); - test("it returns the NO_HOSTNAME symbol for invalid URLs", () => { - expect(fromUrl(":8080/path?query")).toBe(NO_HOSTNAME); - expect(fromUrl("/path?query")).toBe(NO_HOSTNAME); - expect(fromUrl("?query")).toBe(NO_HOSTNAME); - expect(fromUrl("")).toBe(NO_HOSTNAME); - }); + test("it returns the NO_HOSTNAME symbol for invalid URLs", () => { + expect(fromUrl(":8080/path?query")).toBe(NO_HOSTNAME); + expect(fromUrl("/path?query")).toBe(NO_HOSTNAME); + expect(fromUrl("?query")).toBe(NO_HOSTNAME); + expect(fromUrl("")).toBe(NO_HOSTNAME); + }); - test("it handles different schemes", () => { - expect(fromUrl("android-app://com.org.example")).toBe("com.org.example"); - expect(fromUrl("file://root/.config")).toBe("root"); - expect(fromUrl("mailto://person@mail.com")).toBe('mail.com'); - expect(fromUrl("coap+ws://example.com")).toBe("example.com"); - }); + test("it handles different schemes", () => { + expect(fromUrl("android-app://com.org.example")).toBe("com.org.example"); + expect(fromUrl("file://root/.config")).toBe("root"); + expect(fromUrl("mailto://person@mail.com")).toBe("mail.com"); + expect(fromUrl("coap+ws://example.com")).toBe("example.com"); + }); - test("it returns the NO_HOSTNAME symbol for invalid input types", () => { - // @ts-expect-error This is a deliberate error just for the test - expect(fromUrl(undefined)).toBe(NO_HOSTNAME); - // @ts-expect-error This is a deliberate error just for the test - // eslint-disable-next-line no-null/no-null - expect(fromUrl(null)).toBe(NO_HOSTNAME); - // @ts-expect-error This is a deliberate error just for the test - expect(fromUrl(true)).toBe(NO_HOSTNAME); - // @ts-expect-error This is a deliberate error just for the test - expect(fromUrl(1)).toBe(NO_HOSTNAME); - /* eslint-enable */ - }); + test("it returns the NO_HOSTNAME symbol for invalid input types", () => { + // @ts-expect-error This is a deliberate error just for the test + expect(fromUrl(undefined)).toBe(NO_HOSTNAME); + // @ts-expect-error This is a deliberate error just for the test + // eslint-disable-next-line no-null/no-null + expect(fromUrl(null)).toBe(NO_HOSTNAME); + // @ts-expect-error This is a deliberate error just for the test + expect(fromUrl(true)).toBe(NO_HOSTNAME); + // @ts-expect-error This is a deliberate error just for the test + expect(fromUrl(1)).toBe(NO_HOSTNAME); + /* eslint-enable */ + }); }); diff --git a/src/from-url.ts b/src/from-url.ts index dc05cab..6842b4f 100644 --- a/src/from-url.ts +++ b/src/from-url.ts @@ -3,31 +3,31 @@ const urlPattern = /^[a-z][*+.a-z-]+:\/\//i; export const NO_HOSTNAME: unique symbol = Symbol("NO_HOSTNAME"); export const fromUrl = (urlLike: string) => { - /* istanbul ignore next */ - if (typeof URL !== "function") { - throw new Error( - "Looks like the new URL() constructor is not globally available in your environment. Please make sure to use a polyfill.", - ); - } + /* istanbul ignore next */ + if (typeof URL !== "function") { + throw new Error( + "Looks like the new URL() constructor is not globally available in your environment. Please make sure to use a polyfill." + ); + } - // Extra check for non-TypeScript users - if (typeof urlLike !== "string") { - return NO_HOSTNAME; - } + // Extra check for non-TypeScript users + if (typeof urlLike !== "string") { + return NO_HOSTNAME; + } - // URLs that start with // are protocol relative - const url = urlLike.startsWith("//") - ? `http:${urlLike}` - : // URLs that start with / do not have a hostname section - urlLike.startsWith("/") - ? urlLike - : urlPattern.test(urlLike) - ? urlLike - : `http://${urlLike}`; + // URLs that start with // are protocol relative + const url = urlLike.startsWith("//") + ? `http:${urlLike}` + : // URLs that start with / do not have a hostname section + urlLike.startsWith("/") + ? urlLike + : urlPattern.test(urlLike) + ? urlLike + : `http://${urlLike}`; - try { - return new URL(url).hostname; - } catch { - return NO_HOSTNAME; - } + try { + return new URL(url).hostname; + } catch { + return NO_HOSTNAME; + } }; diff --git a/src/main.ts b/src/main.ts index 7a70b06..26f4892 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,13 +1,13 @@ /* istanbul ignore file */ // Jest will report function coverage errors here otherwise export { - parseDomain, - ParseResult, - ParseResultType, - ParseResultInvalid, - ParseResultReserved, - ParseResultNotListed, - ParseResultListed, + parseDomain, + ParseResult, + ParseResultType, + ParseResultInvalid, + ParseResultReserved, + ParseResultNotListed, + ParseResultListed, } from "./parse-domain"; -export {fromUrl, NO_HOSTNAME} from "./from-url"; -export {ValidationError, ValidationErrorType} from "./sanitize"; +export { fromUrl, NO_HOSTNAME } from "./from-url"; +export { ValidationError, ValidationErrorType } from "./sanitize"; diff --git a/src/parse-domain.test.ts b/src/parse-domain.test.ts index 0d4ca90..af50262 100644 --- a/src/parse-domain.test.ts +++ b/src/parse-domain.test.ts @@ -1,467 +1,467 @@ -import {parseDomain, ParseResultType} from "./parse-domain"; -import {ValidationErrorType} from "./sanitize"; -import {fromUrl} from "./from-url"; +import { parseDomain, ParseResultType } from "./parse-domain"; +import { ValidationErrorType } from "./sanitize"; +import { fromUrl } from "./from-url"; const ipV6Samples = [ - "::", - "1::", - "::1", - "1::8", - "1::7:8", - "1:2:3:4:5:6:7:8", - "1:2:3:4:5:6::8", - "1:2:3:4:5:6:7::", - "1:2:3:4:5::7:8", - "1:2:3:4:5::8", - "1:2:3::8", - "1::4:5:6:7:8", - "1::6:7:8", - "1::3:4:5:6:7:8", - "1:2:3:4::6:7:8", - "1:2::4:5:6:7:8", - "::2:3:4:5:6:7:8", - "1:2::8", - "2001:0db8:85a3:0000:0000:8a2e:0370:7334", - "2001:db8:85a3:0:0:8a2e:370:7334", + "::", + "1::", + "::1", + "1::8", + "1::7:8", + "1:2:3:4:5:6:7:8", + "1:2:3:4:5:6::8", + "1:2:3:4:5:6:7::", + "1:2:3:4:5::7:8", + "1:2:3:4:5::8", + "1:2:3::8", + "1::4:5:6:7:8", + "1::6:7:8", + "1::3:4:5:6:7:8", + "1:2:3:4::6:7:8", + "1:2::4:5:6:7:8", + "::2:3:4:5:6:7:8", + "1:2::8", + "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "2001:db8:85a3:0:0:8a2e:370:7334", ]; describe(parseDomain.name, () => { - test("splits a hostname into subDomains, domain and topLevelDomains", () => { - expect(parseDomain("www.example.com")).toMatchObject({ - subDomains: ["www"], - domain: "example", - topLevelDomains: ["com"], - }); - expect(parseDomain("www.example.co.uk")).toMatchObject({ - subDomains: ["www"], - domain: "example", - topLevelDomains: ["co", "uk"], - }); - expect(parseDomain("www.example.co")).toMatchObject({ - subDomains: ["www"], - domain: "example", - topLevelDomains: ["co"], - }); - expect(parseDomain("www.example.com.co")).toMatchObject({ - subDomains: ["www"], - domain: "example", - topLevelDomains: ["com", "co"], - }); - }); + test("splits a hostname into subDomains, domain and topLevelDomains", () => { + expect(parseDomain("www.example.com")).toMatchObject({ + subDomains: ["www"], + domain: "example", + topLevelDomains: ["com"], + }); + expect(parseDomain("www.example.co.uk")).toMatchObject({ + subDomains: ["www"], + domain: "example", + topLevelDomains: ["co", "uk"], + }); + expect(parseDomain("www.example.co")).toMatchObject({ + subDomains: ["www"], + domain: "example", + topLevelDomains: ["co"], + }); + expect(parseDomain("www.example.com.co")).toMatchObject({ + subDomains: ["www"], + domain: "example", + topLevelDomains: ["com", "co"], + }); + }); - test("recognizes private domains as top-level domains", () => { - expect(parseDomain("www.example.cloudfront.net")).toMatchObject({ - subDomains: ["www"], - domain: "example", - topLevelDomains: ["cloudfront", "net"], - }); - }); + test("recognizes private domains as top-level domains", () => { + expect(parseDomain("www.example.cloudfront.net")).toMatchObject({ + subDomains: ["www"], + domain: "example", + topLevelDomains: ["cloudfront", "net"], + }); + }); - test("returns an empty array as subDomains if the hostname has no subDomains", () => { - expect(parseDomain("example.com")).toMatchObject({ - subDomains: [], - domain: "example", - topLevelDomains: ["com"], - }); - expect(parseDomain("example.co.uk")).toMatchObject({ - subDomains: [], - domain: "example", - topLevelDomains: ["co", "uk"], - }); - expect(parseDomain("example.co")).toMatchObject({ - subDomains: [], - domain: "example", - topLevelDomains: ["co"], - }); - expect(parseDomain("example.com.co")).toMatchObject({ - subDomains: [], - domain: "example", - topLevelDomains: ["com", "co"], - }); - }); + test("returns an empty array as subDomains if the hostname has no subDomains", () => { + expect(parseDomain("example.com")).toMatchObject({ + subDomains: [], + domain: "example", + topLevelDomains: ["com"], + }); + expect(parseDomain("example.co.uk")).toMatchObject({ + subDomains: [], + domain: "example", + topLevelDomains: ["co", "uk"], + }); + expect(parseDomain("example.co")).toMatchObject({ + subDomains: [], + domain: "example", + topLevelDomains: ["co"], + }); + expect(parseDomain("example.com.co")).toMatchObject({ + subDomains: [], + domain: "example", + topLevelDomains: ["com", "co"], + }); + }); - test("returns an empty array as subDomains and undefined as domain if the hostname is a top-level domain", () => { - expect(parseDomain("com")).toMatchObject({ - subDomains: [], - domain: undefined, - topLevelDomains: ["com"], - }); - expect(parseDomain("co.uk")).toMatchObject({ - subDomains: [], - domain: undefined, - topLevelDomains: ["co", "uk"], - }); - expect(parseDomain("co")).toMatchObject({ - subDomains: [], - domain: undefined, - topLevelDomains: ["co"], - }); - expect(parseDomain("com.co")).toMatchObject({ - subDomains: [], - domain: undefined, - topLevelDomains: ["com", "co"], - }); - }); + test("returns an empty array as subDomains and undefined as domain if the hostname is a top-level domain", () => { + expect(parseDomain("com")).toMatchObject({ + subDomains: [], + domain: undefined, + topLevelDomains: ["com"], + }); + expect(parseDomain("co.uk")).toMatchObject({ + subDomains: [], + domain: undefined, + topLevelDomains: ["co", "uk"], + }); + expect(parseDomain("co")).toMatchObject({ + subDomains: [], + domain: undefined, + topLevelDomains: ["co"], + }); + expect(parseDomain("com.co")).toMatchObject({ + subDomains: [], + domain: undefined, + topLevelDomains: ["com", "co"], + }); + }); - test("returns ParseResultType.Listed for all variations of common domains", () => { - expect(parseDomain("www.example.com")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("example.com")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("com")).toMatchObject({ - type: ParseResultType.Listed, - }); + test("returns ParseResultType.Listed for all variations of common domains", () => { + expect(parseDomain("www.example.com")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("example.com")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("com")).toMatchObject({ + type: ParseResultType.Listed, + }); - expect(parseDomain("www.example.co.uk")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("example.co.uk")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("co.uk")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("uk")).toMatchObject({ - type: ParseResultType.Listed, - }); + expect(parseDomain("www.example.co.uk")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("example.co.uk")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("co.uk")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("uk")).toMatchObject({ + type: ParseResultType.Listed, + }); - expect(parseDomain("www.example.co")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("example.co")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("co")).toMatchObject({ - type: ParseResultType.Listed, - }); + expect(parseDomain("www.example.co")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("example.co")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("co")).toMatchObject({ + type: ParseResultType.Listed, + }); - expect(parseDomain("www.example.com.co")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("example.com.co")).toMatchObject({ - type: ParseResultType.Listed, - }); - expect(parseDomain("com.co")).toMatchObject({ - type: ParseResultType.Listed, - }); - }); + expect(parseDomain("www.example.com.co")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("example.com.co")).toMatchObject({ + type: ParseResultType.Listed, + }); + expect(parseDomain("com.co")).toMatchObject({ + type: ParseResultType.Listed, + }); + }); - test("returns also a result according to ICANN only", () => { - expect(parseDomain("www.example.co.uk")).toMatchObject({ - icann: { - subDomains: ["www"], - domain: "example", - topLevelDomains: ["co", "uk"], - }, - }); - expect(parseDomain("www.example.cloudfront.net")).toMatchObject({ - icann: { - subDomains: ["www", "example"], - domain: "cloudfront", - topLevelDomains: ["net"], - }, - }); - }); + test("returns also a result according to ICANN only", () => { + expect(parseDomain("www.example.co.uk")).toMatchObject({ + icann: { + subDomains: ["www"], + domain: "example", + topLevelDomains: ["co", "uk"], + }, + }); + expect(parseDomain("www.example.cloudfront.net")).toMatchObject({ + icann: { + subDomains: ["www", "example"], + domain: "cloudfront", + topLevelDomains: ["net"], + }, + }); + }); - test("returns type ParseResultType.Ip for IPv4 addresses", () => { - [ - "0.0.0.0", - "8.8.8.8", - "127.0.0.1", - "100.100.100.100", - "192.168.0.1", - "18.101.25.153", - ].forEach((ipAddress) => { - expect(parseDomain(ipAddress)).toMatchObject({ - type: ParseResultType.Ip, - hostname: ipAddress, - ipVersion: 4, - }); - }); - }); + test("returns type ParseResultType.Ip for IPv4 addresses", () => { + [ + "0.0.0.0", + "8.8.8.8", + "127.0.0.1", + "100.100.100.100", + "192.168.0.1", + "18.101.25.153", + ].forEach((ipAddress) => { + expect(parseDomain(ipAddress)).toMatchObject({ + type: ParseResultType.Ip, + hostname: ipAddress, + ipVersion: 4, + }); + }); + }); - test("returns type ParseResultType.Ip for IPv6 addresses", () => { - ipV6Samples.forEach((ipAddress) => { - expect(parseDomain(ipAddress)).toMatchObject({ - type: ParseResultType.Ip, - hostname: ipAddress, - ipVersion: 6, - }); - }); - }); + test("returns type ParseResultType.Ip for IPv6 addresses", () => { + ipV6Samples.forEach((ipAddress) => { + expect(parseDomain(ipAddress)).toMatchObject({ + type: ParseResultType.Ip, + hostname: ipAddress, + ipVersion: 6, + }); + }); + }); - test("returns type ParseResultType.Ip for IPv6 addresses with square brackets according to RFC 3986", () => { - ipV6Samples.forEach((ipAddress) => { - expect(parseDomain("[" + ipAddress + "]")).toMatchObject({ - type: ParseResultType.Ip, - hostname: ipAddress, - ipVersion: 6, - }); - }); - }); + test("returns type ParseResultType.Ip for IPv6 addresses with square brackets according to RFC 3986", () => { + ipV6Samples.forEach((ipAddress) => { + expect(parseDomain("[" + ipAddress + "]")).toMatchObject({ + type: ParseResultType.Ip, + hostname: ipAddress, + ipVersion: 6, + }); + }); + }); - test("returns type ParseResultType.Reserved for all reserved TLDs according to RFC 6761, 6762", () => { - expect(parseDomain("example")).toMatchObject({ - type: ParseResultType.Reserved, - labels: ["example"], - }); - // We decided to treat .invalid domains not in a special way. - expect(parseDomain("some.invalid")).toMatchObject({ - type: ParseResultType.Reserved, - labels: ["some", "invalid"], - }); - expect(parseDomain("some.localhost")).toMatchObject({ - type: ParseResultType.Reserved, - labels: ["some", "localhost"], - }); - expect(parseDomain("test")).toMatchObject({ - type: ParseResultType.Reserved, - labels: ["test"], - }); - expect(parseDomain("www.some.local")).toMatchObject({ - type: ParseResultType.Reserved, - labels: ["www", "some", "local"], - }); - }); + test("returns type ParseResultType.Reserved for all reserved TLDs according to RFC 6761, 6762", () => { + expect(parseDomain("example")).toMatchObject({ + type: ParseResultType.Reserved, + labels: ["example"], + }); + // We decided to treat .invalid domains not in a special way. + expect(parseDomain("some.invalid")).toMatchObject({ + type: ParseResultType.Reserved, + labels: ["some", "invalid"], + }); + expect(parseDomain("some.localhost")).toMatchObject({ + type: ParseResultType.Reserved, + labels: ["some", "localhost"], + }); + expect(parseDomain("test")).toMatchObject({ + type: ParseResultType.Reserved, + labels: ["test"], + }); + expect(parseDomain("www.some.local")).toMatchObject({ + type: ParseResultType.Reserved, + labels: ["www", "some", "local"], + }); + }); - test("returns type ParseResultType.Reserved for an empty string", () => { - expect(parseDomain("")).toMatchObject({ - type: ParseResultType.Reserved, - labels: [], - }); - }); + test("returns type ParseResultType.Reserved for an empty string", () => { + expect(parseDomain("")).toMatchObject({ + type: ParseResultType.Reserved, + labels: [], + }); + }); - test("returns type ParseResultType.NotListed for valid hostnames that are not listed", () => { - expect(parseDomain("valid")).toMatchObject({ - type: ParseResultType.NotListed, - labels: ["valid"], - }); - expect(parseDomain("this.is.not-listed")).toMatchObject({ - type: ParseResultType.NotListed, - labels: ["this", "is", "not-listed"], - }); - }); + test("returns type ParseResultType.NotListed for valid hostnames that are not listed", () => { + expect(parseDomain("valid")).toMatchObject({ + type: ParseResultType.NotListed, + labels: ["valid"], + }); + expect(parseDomain("this.is.not-listed")).toMatchObject({ + type: ParseResultType.NotListed, + labels: ["this", "is", "not-listed"], + }); + }); - test("returns type ParseResultType.Invalid and error information for a hostname with an empty label", () => { - expect(parseDomain(".example.com")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelMinLength, - message: - 'Label "" is too short. Label is 0 octets long but should be at least 1.', - column: 1, - }), - ]), - }); - expect(parseDomain("www..example.com")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - column: 5, - }), - ]), - }); - }); + test("returns type ParseResultType.Invalid and error information for a hostname with an empty label", () => { + expect(parseDomain(".example.com")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelMinLength, + message: + 'Label "" is too short. Label is 0 octets long but should be at least 1.', + column: 1, + }), + ]), + }); + expect(parseDomain("www..example.com")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + column: 5, + }), + ]), + }); + }); - test("returns type ParseResultType.Invalid for the domain '.'", () => { - expect(parseDomain(".")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelMinLength, - }), - ]), - }); - }); + test("returns type ParseResultType.Invalid for the domain '.'", () => { + expect(parseDomain(".")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelMinLength, + }), + ]), + }); + }); - test("returns type ParseResultType.Invalid and error information for a hostname with a label that is too long", () => { - const labelToLong = new Array(64).fill("x").join(""); + test("returns type ParseResultType.Invalid and error information for a hostname with a label that is too long", () => { + const labelToLong = new Array(64).fill("x").join(""); - expect(parseDomain(`${labelToLong}.example.com`)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelMaxLength, - message: `Label "${labelToLong}" is too long. Label is 64 octets long but should not be longer than 63.`, - column: 1, - }), - ]), - }); - expect(parseDomain(`www.${labelToLong}.example.com`)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - column: 5, - }), - ]), - }); - }); + expect(parseDomain(`${labelToLong}.example.com`)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelMaxLength, + message: `Label "${labelToLong}" is too long. Label is 64 octets long but should not be longer than 63.`, + column: 1, + }), + ]), + }); + expect(parseDomain(`www.${labelToLong}.example.com`)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + column: 5, + }), + ]), + }); + }); - test("returns type ParseResultType.Invalid and error information for a hostname that is too long", () => { - const domain = new Array(127).fill("x").join(".") + "x"; + test("returns type ParseResultType.Invalid and error information for a hostname that is too long", () => { + const domain = new Array(127).fill("x").join(".") + "x"; - expect(parseDomain(domain)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.DomainMaxLength, - message: `Domain "${domain}" is too long. Domain is 254 octets long but should not be longer than 253.`, - column: 254, - }), - ]), - }); - }); + expect(parseDomain(domain)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.DomainMaxLength, + message: `Domain "${domain}" is too long. Domain is 254 octets long but should not be longer than 253.`, + column: 254, + }), + ]), + }); + }); - test("returns type ParseResultType.Invalid and error information for a hostname that contains an invalid character", () => { - // Deliberately testing the '[' and ']' character that is removed from IPv6 addresses - expect(parseDomain("[")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelInvalidCharacter, - message: 'Label "[" contains invalid character "[" at column 1.', - column: 1, - }), - ]), - }); - expect(parseDomain("some-label]")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelInvalidCharacter, - message: - 'Label "some-label]" contains invalid character "]" at column 11.', - column: 11, - }), - ]), - }); - expect(parseDomain("some-静")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelInvalidCharacter, - message: - 'Label "some-静" contains invalid character "静" at column 6.', - column: 6, - }), - ]), - }); - expect(parseDomain("some-静.com")).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.LabelInvalidCharacter, - message: - 'Label "some-静" contains invalid character "静" at column 6.', - column: 6, - }), - ]), - }); - }); + test("returns type ParseResultType.Invalid and error information for a hostname that contains an invalid character", () => { + // Deliberately testing the '[' and ']' character that is removed from IPv6 addresses + expect(parseDomain("[")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelInvalidCharacter, + message: 'Label "[" contains invalid character "[" at column 1.', + column: 1, + }), + ]), + }); + expect(parseDomain("some-label]")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelInvalidCharacter, + message: + 'Label "some-label]" contains invalid character "]" at column 11.', + column: 11, + }), + ]), + }); + expect(parseDomain("some-静")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelInvalidCharacter, + message: + 'Label "some-静" contains invalid character "静" at column 6.', + column: 6, + }), + ]), + }); + expect(parseDomain("some-静.com")).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.LabelInvalidCharacter, + message: + 'Label "some-静" contains invalid character "静" at column 6.', + column: 6, + }), + ]), + }); + }); - test("returns type ParseResultType.Invalid and error information if the input was not domain like", () => { - // @ts-expect-error This is a deliberate error for the test - expect(parseDomain(undefined)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.NoHostname, - message: "The given input undefined does not look like a hostname.", - column: 1, - }), - ]), - }); - // @ts-expect-error This is a deliberate error for the test - // eslint-disable-next-line no-null/no-null - expect(parseDomain(null)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.NoHostname, - message: "The given input null does not look like a hostname.", - column: 1, - }), - ]), - }); - // @ts-expect-error This is a deliberate error for the test - expect(parseDomain(true)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.NoHostname, - message: "The given input true does not look like a hostname.", - column: 1, - }), - ]), - }); - // @ts-expect-error This is a deliberate error for the test - expect(parseDomain(1)).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.NoHostname, - message: "The given input 1 does not look like a hostname.", - column: 1, - }), - ]), - }); - }); + test("returns type ParseResultType.Invalid and error information if the input was not domain like", () => { + // @ts-expect-error This is a deliberate error for the test + expect(parseDomain(undefined)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.NoHostname, + message: "The given input undefined does not look like a hostname.", + column: 1, + }), + ]), + }); + // @ts-expect-error This is a deliberate error for the test + // eslint-disable-next-line no-null/no-null + expect(parseDomain(null)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.NoHostname, + message: "The given input null does not look like a hostname.", + column: 1, + }), + ]), + }); + // @ts-expect-error This is a deliberate error for the test + expect(parseDomain(true)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.NoHostname, + message: "The given input true does not look like a hostname.", + column: 1, + }), + ]), + }); + // @ts-expect-error This is a deliberate error for the test + expect(parseDomain(1)).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.NoHostname, + message: "The given input 1 does not look like a hostname.", + column: 1, + }), + ]), + }); + }); - test("returns type ParseResultType.Invalid and error information if the input was not an URL", () => { - expect(parseDomain(fromUrl(""))).toMatchObject({ - type: ParseResultType.Invalid, - errors: expect.arrayContaining([ - expect.objectContaining({ - type: ValidationErrorType.NoHostname, - message: - "The given input Symbol(NO_HOSTNAME) does not look like a hostname.", - column: 1, - }), - ]), - }); - }); + test("returns type ParseResultType.Invalid and error information if the input was not an URL", () => { + expect(parseDomain(fromUrl(""))).toMatchObject({ + type: ParseResultType.Invalid, + errors: expect.arrayContaining([ + expect.objectContaining({ + type: ValidationErrorType.NoHostname, + message: + "The given input Symbol(NO_HOSTNAME) does not look like a hostname.", + column: 1, + }), + ]), + }); + }); - test("accepts a trailing dot as valid root label", () => { - expect(parseDomain("www.example.com.")).toMatchObject({ - subDomains: ["www"], - domain: "example", - topLevelDomains: ["com"], - }); - }); + test("accepts a trailing dot as valid root label", () => { + expect(parseDomain("www.example.com.")).toMatchObject({ + subDomains: ["www"], + domain: "example", + topLevelDomains: ["com"], + }); + }); - test("accepts uppercase letters and preserves the case", () => { - expect(parseDomain("WWW.EXAMPLE.COM")).toMatchObject({ - subDomains: ["WWW"], - domain: "EXAMPLE", - topLevelDomains: ["COM"], - }); - }); + test("accepts uppercase letters and preserves the case", () => { + expect(parseDomain("WWW.EXAMPLE.COM")).toMatchObject({ + subDomains: ["WWW"], + domain: "EXAMPLE", + topLevelDomains: ["COM"], + }); + }); - test("accepts hyphens", () => { - expect(parseDomain("w-w-w.e-x-a-m-p-l-e.com")).toMatchObject({ - subDomains: ["w-w-w"], - domain: "e-x-a-m-p-l-e", - topLevelDomains: ["com"], - }); - }); + test("accepts hyphens", () => { + expect(parseDomain("w-w-w.e-x-a-m-p-l-e.com")).toMatchObject({ + subDomains: ["w-w-w"], + domain: "e-x-a-m-p-l-e", + topLevelDomains: ["com"], + }); + }); - test("accepts numbers", () => { - expect(parseDomain("123.456.com")).toMatchObject({ - subDomains: ["123"], - domain: "456", - topLevelDomains: ["com"], - }); - }); + test("accepts numbers", () => { + expect(parseDomain("123.456.com")).toMatchObject({ + subDomains: ["123"], + domain: "456", + topLevelDomains: ["com"], + }); + }); - test("returns the input hostname in the result", () => { - expect(parseDomain("www.EXAMPLE.com")).toMatchObject({ - hostname: "www.EXAMPLE.com", - }); - expect(parseDomain("www.EXAMPLE.test")).toMatchObject({ - hostname: "www.EXAMPLE.test", - }); - }); + test("returns the input hostname in the result", () => { + expect(parseDomain("www.EXAMPLE.com")).toMatchObject({ + hostname: "www.EXAMPLE.com", + }); + expect(parseDomain("www.EXAMPLE.test")).toMatchObject({ + hostname: "www.EXAMPLE.test", + }); + }); }); diff --git a/src/parse-domain.ts b/src/parse-domain.ts index ca60677..be972b1 100644 --- a/src/parse-domain.ts +++ b/src/parse-domain.ts @@ -1,48 +1,48 @@ -import {icannTrie, privateTrie} from "./serialized-tries"; -import {lookUpTldsInTrie} from "./trie/look-up"; +import { icannTrie, privateTrie } from "./serialized-tries"; +import { lookUpTldsInTrie } from "./trie/look-up"; import { - ValidationError, - sanitize, - SanitizationResultType, - SanitizationResultValidIp, + ValidationError, + sanitize, + SanitizationResultType, + SanitizationResultValidIp, } from "./sanitize"; -import {TrieRootNode} from "./trie/nodes"; -import {parseTrie} from "./trie/parse-trie"; -import {NO_HOSTNAME} from "./from-url"; +import { TrieRootNode } from "./trie/nodes"; +import { parseTrie } from "./trie/parse-trie"; +import { NO_HOSTNAME } from "./from-url"; export const RESERVED_TOP_LEVEL_DOMAINS = [ - "localhost", - "local", - "example", - "invalid", - "test", + "localhost", + "local", + "example", + "invalid", + "test", ]; export type Label = string; export enum ParseResultType { - /** - * This parse result is returned in case the given hostname does not adhere to [RFC 1034](https://tools.ietf.org/html/rfc1034). - */ - Invalid = "INVALID", - /** - * This parse result is returned if the given hostname was an IPv4 or IPv6. - */ - Ip = "IP", - /** - * This parse result is returned when the given hostname - * - is the root domain (the empty string `""`) - * - belongs to the top-level domain `localhost`, `local`, `example`, `invalid` or `test` - */ - Reserved = "RESERVED", - /** - * This parse result is returned when the given hostname is valid and does not belong to a reserved top-level domain, but is not listed in the public suffix list. - */ - NotListed = "NOT_LISTED", - /** - * This parse result is returned when the given hostname belongs to a top-level domain that is listed in the public suffix list. - */ - Listed = "LISTED", + /** + * This parse result is returned in case the given hostname does not adhere to [RFC 1034](https://tools.ietf.org/html/rfc1034). + */ + Invalid = "INVALID", + /** + * This parse result is returned if the given hostname was an IPv4 or IPv6. + */ + Ip = "IP", + /** + * This parse result is returned when the given hostname + * - is the root domain (the empty string `""`) + * - belongs to the top-level domain `localhost`, `local`, `example`, `invalid` or `test` + */ + Reserved = "RESERVED", + /** + * This parse result is returned when the given hostname is valid and does not belong to a reserved top-level domain, but is not listed in the public suffix list. + */ + NotListed = "NOT_LISTED", + /** + * This parse result is returned when the given hostname belongs to a top-level domain that is listed in the public suffix list. + */ + Listed = "LISTED", } // The following parse result types are organized in this complicated way @@ -50,90 +50,88 @@ export enum ParseResultType { // If we copy the types (hence duplicating the shared properties), // JSDoc comments would show up duplicated in the final return type as well. type ParseResultCommon = { - /** - * The type of the parse result. Use switch or if to distinguish between different results. - */ - type: Type; - /** - * The original hostname that was passed to parseDomain(). - */ - hostname: Type extends ParseResultType.Invalid - ? string | typeof NO_HOSTNAME - : string; + /** + * The type of the parse result. Use switch or if to distinguish between different results. + */ + type: Type; + /** + * The original hostname that was passed to parseDomain(). + */ + hostname: Type extends ParseResultType.Invalid + ? string | typeof NO_HOSTNAME + : string; }; export type ParseResultInvalid = ParseResultCommon & { - /** - * An array of validation errors. - */ - errors: Array; + /** + * An array of validation errors. + */ + errors: Array; }; type ParseResultCommonValidDomain = { - /** - * An array of labels that were separated by a dot character in the given hostname. - */ - labels: Array