Skip to content

Instantly share code, notes, and snippets.

@eligrey
Created September 25, 2021 19:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eligrey/443d51fab55864005ffb3873204b877a to your computer and use it in GitHub Desktop.
Save eligrey/443d51fab55864005ffb3873204b877a to your computer and use it in GitHub Desktop.
URL validation utilities
/**
* Validate potentially relative URL
*
* @param input - URL to validate
* @returns true if URL is valid and doesn't need additional encoding
*/
const isValidURL = (input: string): boolean => {
try {
const { href, pathname, host, origin } = new globalThis.URL(
input,
'https://-',
);
// If href value changes (absolute) or pathname value changes (relative),
// then the URL is invalid or requires additional encoding
return host === '-'
? // relative URL
input === pathname
: // absolute URL
input === href || input === origin;
} catch (ex) {
return false;
}
};
const removeProtocol = ({
href,
protocol,
}: Pick<URL, 'href' | 'protocol'>): string =>
href.slice(protocol.length).replace(/^\/*/, '');
const matchWithOptionalEndingSlash = (input: string, parsed: string): boolean =>
input === parsed || (!input.endsWith('/') && parsed === `${input}/`);
/**
* Check whether a given URL is absolute and valid
*
* @param input - URL to check
* @returns Whether the URL is absolute and valid
*/
const isValidAbsoluteURL = (input: string): boolean => {
try {
const url = new URL(input);
const { href, protocol, origin } = url;
// origin matches
if (input.startsWith(origin)) {
return matchWithOptionalEndingSlash(input, href);
}
// origin doesn't match (extra slashes were potentially removed)
if (input.startsWith(protocol)) {
return matchWithOptionalEndingSlash(
removeProtocol({ href: input, protocol }),
removeProtocol(url),
);
}
return false;
} catch (ex) {
return false;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment