build-url-ts
Version:
A small library that builds a URL given its components
126 lines (122 loc) • 5.53 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function customEncodeURIComponent(str) {
return encodeURIComponent(str).replace(/'/g, "%27").replace(/`/g, "%60");
}
function buildQueryString(queryParams, lowerCase, disableCSV, useCustomEncoding = true) {
const encode = (input) => useCustomEncoding ? customEncodeURIComponent(input) : encodeURIComponent(input);
const queryParts = [];
for (const [key, value] of Object.entries(queryParams)) {
if (value === void 0) continue;
const encodedKey = encode(lowerCase ? key.toLowerCase() : key);
if (!Array.isArray(value)) {
queryParts.push(`${encodedKey}=${encode(formatValue(value, lowerCase))}`);
continue;
}
const items = value.filter((item) => item !== void 0);
if (items.length === 0) continue;
if (!disableCSV) {
const csvValue = items.map((item) => formatValue(item, lowerCase)).join(",");
queryParts.push(`${encodedKey}=${encode(csvValue)}`);
continue;
}
let index = disableCSV === "order_desc" ? items.length - 1 : 0;
for (const item of items) {
const encodedValue = encode(formatValue(item, lowerCase));
switch (disableCSV) {
case "array":
queryParts.push(`${encodedKey}[]=${encodedValue}`);
break;
case "order_asc":
queryParts.push(`${encodedKey}[${index++}]=${encodedValue}`);
break;
case "order_desc":
queryParts.push(`${encodedKey}[${index--}]=${encodedValue}`);
break;
default:
queryParts.push(`${encodedKey}=${encodedValue}`);
break;
}
}
}
return queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
}
function formatValue(value, lowerCase) {
if (value === null || value === void 0) return "";
if (typeof value === "boolean") return value.toString();
if (value === 0) return "0";
if (typeof value === "number" && Number.isNaN(value)) return "NaN";
if (!value) return "";
const stringValue = value instanceof Date ? value.toString() : typeof value === "object" && !Array.isArray(value) ? JSON.stringify(value) : String(value).trim();
return lowerCase ? stringValue.toLowerCase() : stringValue;
}
function appendPath(path, builtUrl, lowerCase) {
const url = builtUrl != null ? builtUrl : "";
const trimmedPath = String(path).trim();
const pathString = lowerCase ? trimmedPath.toLowerCase() : trimmedPath;
if (!pathString) return url;
if (pathString === "/") {
return url.endsWith("/") ? url : `${url}/`;
}
const hasTrailingSlash = pathString.endsWith("/");
const cleanedPath = pathString.split("/").filter((segment) => segment.length > 0).join("/");
const finalPath = hasTrailingSlash && cleanedPath ? `${cleanedPath}/` : cleanedPath;
const baseUrl = url.replace(/\/+$/, "");
return finalPath ? `${baseUrl}/${finalPath}` : baseUrl;
}
function buildHash(hash, lowerCase) {
const trimmedHash = String(hash).trim();
if (!trimmedHash) return "";
const hashString = `#${trimmedHash}`;
return lowerCase ? hashString.toLowerCase() : hashString;
}
function parseUrl(url) {
const queryParams = {};
const hashIndex = url.indexOf("#");
const hash = hashIndex === -1 ? "" : url.substring(hashIndex + 1);
const withoutHash = hashIndex === -1 ? url : url.substring(0, hashIndex);
const queryIndex = withoutHash.indexOf("?");
if (queryIndex === -1) {
return { baseUrl: withoutHash, queryParams, hash };
}
const baseUrl = withoutHash.substring(0, queryIndex);
const queryString = withoutHash.substring(queryIndex + 1);
for (const pair of queryString.split("&")) {
if (!pair) continue;
const eqIndex = pair.indexOf("=");
const rawKey = eqIndex === -1 ? pair : pair.substring(0, eqIndex);
const rawValue = eqIndex === -1 ? "" : pair.substring(eqIndex + 1);
if (rawKey) {
queryParams[decodeURIComponent(rawKey)] = rawValue ? decodeURIComponent(rawValue) : "";
}
}
return { baseUrl, queryParams, hash };
}
function buildUrl(url, options) {
var _a, _b;
const hasStringUrl = typeof url === "string";
const parsed = hasStringUrl ? parseUrl(url) : null;
const buildOptions = url !== null && typeof url === "object" ? url : options;
let result = (_a = parsed == null ? void 0 : parsed.baseUrl) != null ? _a : "";
const segments = [];
if (buildOptions == null ? void 0 : buildOptions.path) segments.push(buildOptions.path);
if (buildOptions == null ? void 0 : buildOptions.paths) segments.push(...buildOptions.paths);
for (const segment of segments) {
result = appendPath(segment, result, buildOptions == null ? void 0 : buildOptions.lowerCase);
}
const allQueryParams = { ...parsed == null ? void 0 : parsed.queryParams, ...buildOptions == null ? void 0 : buildOptions.queryParams };
if (Object.keys(allQueryParams).length > 0) {
result += buildQueryString(allQueryParams, buildOptions == null ? void 0 : buildOptions.lowerCase, buildOptions == null ? void 0 : buildOptions.disableCSV, false);
}
const finalHash = (buildOptions == null ? void 0 : buildOptions.hash) !== void 0 ? buildOptions.hash : (_b = parsed == null ? void 0 : parsed.hash) != null ? _b : "";
if (finalHash) {
result += buildHash(finalHash, buildOptions == null ? void 0 : buildOptions.lowerCase);
}
return result;
}
exports.appendPath = appendPath;
exports.buildHash = buildHash;
exports.buildQueryString = buildQueryString;
exports.buildUrl = buildUrl;
exports.default = buildUrl;
//# sourceMappingURL=index.cjs.map