@automattic/calypso-url
Version:
This library provides both generic and Calypso-specific utilities for handling URLs.
105 lines • 3.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUrlFromParts = exports.getUrlParts = void 0;
const url_type_1 = require("./url-type");
const BASE_URL = `http://__domain__.invalid`;
const EMPTY_URL = Object.freeze({
protocol: '',
host: '',
hostname: '',
port: '',
origin: '',
pathname: '',
hash: '',
search: '',
searchParams: new URLSearchParams(),
username: '',
password: '',
});
const URL_PART_KEYS = Object.keys(EMPTY_URL);
function pickUrlParts(parsedUrl, include = URL_PART_KEYS) {
const pickedUrl = { ...EMPTY_URL };
include.forEach((name) => {
pickedUrl[name] = parsedUrl?.[name] ?? EMPTY_URL[name];
});
return pickedUrl;
}
/**
* Returns the various available URL parts.
*
* @param url the URL to analyze
* @returns the URL parts
*/
function getUrlParts(url) {
const urlType = (0, url_type_1.determineUrlType)(url);
// Invalid URL; return empty URL parts.
if (urlType === url_type_1.URL_TYPE.INVALID) {
return { ...EMPTY_URL };
}
const parsed = url instanceof URL ? url : new URL(url, BASE_URL);
// Absolute URL; pick all parts.
if (urlType === url_type_1.URL_TYPE.ABSOLUTE) {
return pickUrlParts(parsed);
}
// Scheme-relative URL; pick everything except the protocol and origin.
if (urlType === url_type_1.URL_TYPE.SCHEME_RELATIVE) {
return pickUrlParts(parsed, URL_PART_KEYS.filter((item) => item !== 'protocol' && item !== 'origin'));
}
// Path-absolute or path-relative URL; pick only a few parts.
const pathPartKeys = ['pathname', 'hash', 'search', 'searchParams'];
const pathParts = pickUrlParts(parsed, pathPartKeys);
// Path-relative URLs require special handling, because they cannot be transformed
// into an absolute URL without potentially losing path information.
// E.g. `../foo?bar=baz` becomes `<base>/foo?bar=baz` when fed to `new URL()`
// with a base, losing the traversal into the parent directory.
// We need to handle these with string functions instead.
if (urlType === url_type_1.URL_TYPE.PATH_RELATIVE) {
pathParts.pathname = url.split(/[?#]/, 1)[0];
}
return pathParts;
}
exports.getUrlParts = getUrlParts;
/**
* Returns a URL object built from the provided URL parts.
*
* @param parts the provided URL parts.
* @returns the generated URL object.
*/
function getUrlFromParts(parts) {
if (!parts?.protocol && !parts?.origin) {
throw new Error('getUrlFromParts: protocol missing.');
}
if (!parts.host && !parts.hostname && !parts.origin) {
throw new Error('getUrlFromParts: host missing.');
}
const result = new URL(BASE_URL);
// Apply 'origin' first, since it includes protocol, hostname, and port.
if (parts.origin) {
try {
const origin = new URL(parts.origin);
result.host = origin.host;
result.protocol = origin.protocol;
}
catch {
throw new Error('getUrlFromParts: invalid origin.');
}
}
// Apply 'host' next, since it includes both hostname and port.
result.host = parts.host || result.host;
// Apply 'searchParams' object if it's specified.
if (parts.searchParams?.toString) {
result.search = parts.searchParams.toString();
}
for (const part of URL_PART_KEYS) {
if (part !== 'host' && part !== 'origin' && part !== 'searchParams') {
const value = parts[part];
// Avoid some implementation bugs, e.g. https://bugs.webkit.org/show_bug.cgi?id=127958.
if (value && value !== result[part]) {
result[part] = value;
}
}
}
return result;
}
exports.getUrlFromParts = getUrlFromParts;
//# sourceMappingURL=url-parts.js.map