Spaces:
Paused
Paused
| /** | |
| * Implement a factory allowing to plug different implementations of suffix | |
| * lookup (e.g.: using a trie or the packed hashes datastructures). This is used | |
| * and exposed in `tldts.ts` and `tldts-experimental.ts` bundle entrypoints. | |
| */ | |
| import getDomain from './domain'; | |
| import getDomainWithoutSuffix from './domain-without-suffix'; | |
| import extractHostname, { | |
| extractedHostnameValidated, | |
| } from './extract-hostname'; | |
| import isIp from './is-ip'; | |
| import isSpecialUse from './is-special-use'; | |
| import isValidHostname from './is-valid'; | |
| import { IPublicSuffix, ISuffixLookupOptions } from './lookup/interface'; | |
| import { IOptions, setDefaults } from './options'; | |
| import getSubdomain from './subdomain'; | |
| export interface IResult { | |
| // `hostname` is either a registered name (including but not limited to a | |
| // hostname), or an IP address, directly extracted from the input URL. IPv4 | |
| // addresses are in dot-decimal notation. IPv6 is returned without its | |
| // surrounding brackets; both bracketed (in URLs, e.g. `http://[::1]/`) and | |
| // bare unbracketed (e.g. `2a01:e35::1`) IPv6 literals are accepted. | |
| hostname: string | null; | |
| // Is `hostname` an IP? (IPv4 or IPv6) | |
| isIp: boolean | null; | |
| // `hostname` split between subdomain, domain and its public suffix (if any) | |
| subdomain: string | null; | |
| domain: string | null; | |
| publicSuffix: string | null; | |
| domainWithoutSuffix: string | null; | |
| // Specifies if `publicSuffix` comes from the ICANN or PRIVATE section of the list | |
| isIcann: boolean | null; | |
| isPrivate: boolean | null; | |
| // Is `hostname` a special-use domain from the IANA registry (RFC 6761 et al.: | |
| // e.g. `localhost`, `*.test`, `*.local`, `*.onion`, `home.arpa`)? `isIcann`/ | |
| // `isPrivate` do not identify these (most are not in the Public Suffix List; | |
| // the few that are appear as ordinary ICANN suffixes). `null` unless the | |
| // `detectSpecialUse` option is enabled (see is-special-use.ts). | |
| isSpecialUse: boolean | null; | |
| } | |
| export function getEmptyResult(): IResult { | |
| return { | |
| domain: null, | |
| domainWithoutSuffix: null, | |
| hostname: null, | |
| isIcann: null, | |
| isIp: null, | |
| isPrivate: null, | |
| isSpecialUse: null, | |
| publicSuffix: null, | |
| subdomain: null, | |
| }; | |
| } | |
| export function resetResult(result: IResult): void { | |
| result.domain = null; | |
| result.domainWithoutSuffix = null; | |
| result.hostname = null; | |
| result.isIcann = null; | |
| result.isIp = null; | |
| result.isPrivate = null; | |
| result.isSpecialUse = null; | |
| result.publicSuffix = null; | |
| result.subdomain = null; | |
| } | |
| // Flags representing steps in the `parse` function. They are used to implement | |
| // an early stop mechanism (simulating some form of laziness) to avoid doing | |
| // more work than necessary to perform a given action (e.g.: we don't need to | |
| // extract the domain and subdomain if we are only interested in public suffix). | |
| export const enum FLAG { | |
| HOSTNAME, | |
| IS_VALID, | |
| PUBLIC_SUFFIX, | |
| DOMAIN, | |
| SUB_DOMAIN, | |
| ALL, | |
| } | |
| export function parseImpl( | |
| url: string, | |
| step: FLAG, | |
| suffixLookup: ( | |
| _1: string, | |
| _2: ISuffixLookupOptions, | |
| _3: IPublicSuffix, | |
| ) => void, | |
| partialOptions: Partial<IOptions> | undefined, | |
| result: IResult, | |
| ): IResult { | |
| const options: IOptions = /*@__INLINE__*/ setDefaults(partialOptions); | |
| // Very fast approximate check to make sure `url` is a string. This is needed | |
| // because the library will not necessarily be used in a typed setup and | |
| // values of arbitrary types might be given as argument. | |
| if (typeof url !== 'string') { | |
| return result; | |
| } | |
| // Extract hostname from `url` only if needed. This can be made optional | |
| // using `options.extractHostname`. This option will typically be used | |
| // whenever we are sure the inputs to `parse` are already hostnames and not | |
| // arbitrary URLs. | |
| // | |
| // `mixedInput` allows to specify if we expect a mix of URLs and hostnames | |
| // as input. If only hostnames are expected then `extractHostname` can be | |
| // set to `false` to speed-up parsing. If only URLs are expected then | |
| // `mixedInputs` can be set to `false`. The `mixedInputs` is only a hint | |
| // and will not change the behavior of the library. | |
| // Whether `url` itself was already a valid hostname (only computed on the | |
| // mixedInputs path). Lets us skip the post-extraction validation below when | |
| // extractHostname returned `url` unchanged (same reference). | |
| let urlIsValid = false; | |
| if (!options.extractHostname) { | |
| result.hostname = url; | |
| } else if (options.mixedInputs) { | |
| urlIsValid = isValidHostname(url); | |
| result.hostname = extractHostname( | |
| url, | |
| urlIsValid, | |
| options.validateHostname, | |
| ); | |
| } else { | |
| result.hostname = extractHostname(url, false, options.validateHostname); | |
| } | |
| // Check if `hostname` is a valid ip address | |
| if (options.detectIp && result.hostname !== null) { | |
| result.isIp = isIp(result.hostname); | |
| if (result.isIp) { | |
| return result; | |
| } | |
| } | |
| // Perform hostname validation if enabled. If hostname is not valid, no need to | |
| // go further as there will be no valid domain or sub-domain. This validation | |
| // is applied before any early returns to ensure consistent behavior across | |
| // all API methods including getHostname(). | |
| if ( | |
| options.validateHostname && | |
| options.extractHostname && | |
| result.hostname !== null && | |
| // Skip the re-scan when `url` was already validated and extractHostname | |
| // returned it unchanged (same reference => identical string, still valid). | |
| !(urlIsValid && result.hostname === url) && | |
| // Skip the re-scan when extractHostname already validated the host inline | |
| // (a confirmed-valid simple authority — see extract-hostname.ts). | |
| !extractedHostnameValidated && | |
| !isValidHostname(result.hostname) | |
| ) { | |
| result.hostname = null; | |
| return result; | |
| } | |
| if (step === FLAG.HOSTNAME || result.hostname === null) { | |
| return result; | |
| } | |
| // Flag special-use domains, only when opted in (`detectSpecialUse`) and only | |
| // for the full `parse()` result (FLAG.ALL). Computed here, before the | |
| // public-suffix/domain early-returns below, so single-label names like | |
| // `localhost` (which have no registrable domain) are still flagged. | |
| if (step === FLAG.ALL && options.detectSpecialUse) { | |
| result.isSpecialUse = isSpecialUse(result.hostname); | |
| } | |
| // Extract public suffix | |
| suffixLookup(result.hostname, options, result); | |
| if (step === FLAG.PUBLIC_SUFFIX || result.publicSuffix === null) { | |
| return result; | |
| } | |
| // Extract domain | |
| result.domain = getDomain(result.publicSuffix, result.hostname, options); | |
| if (step === FLAG.DOMAIN || result.domain === null) { | |
| return result; | |
| } | |
| // Extract subdomain | |
| result.subdomain = getSubdomain(result.hostname, result.domain); | |
| if (step === FLAG.SUB_DOMAIN) { | |
| return result; | |
| } | |
| // Extract domain without suffix | |
| result.domainWithoutSuffix = getDomainWithoutSuffix( | |
| result.domain, | |
| result.publicSuffix, | |
| ); | |
| return result; | |
| } | |