132 lines
3.8 KiB
JavaScript
132 lines
3.8 KiB
JavaScript
const PLUS_RE = /\+/g;
|
|
function decode(text = "") {
|
|
try {
|
|
return decodeURIComponent("" + text);
|
|
} catch {
|
|
return "" + text;
|
|
}
|
|
}
|
|
function decodeQueryValue(text) {
|
|
return decode(text.replace(PLUS_RE, " "));
|
|
}
|
|
|
|
function parseQuery(parametersString = "") {
|
|
const object = {};
|
|
if (parametersString[0] === "?") {
|
|
parametersString = parametersString.slice(1);
|
|
}
|
|
for (const parameter of parametersString.split("&")) {
|
|
const s = parameter.match(/([^=]+)=?(.*)/) || [];
|
|
if (s.length < 2) {
|
|
continue;
|
|
}
|
|
const key = decode(s[1]);
|
|
if (key === "__proto__" || key === "constructor") {
|
|
continue;
|
|
}
|
|
const value = decodeQueryValue(s[2] || "");
|
|
if (object[key]) {
|
|
if (Array.isArray(object[key])) {
|
|
object[key].push(value);
|
|
} else {
|
|
object[key] = [object[key], value];
|
|
}
|
|
} else {
|
|
object[key] = value;
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
const PROTOCOL_REGEX = /^\w+:(\/\/)?/;
|
|
const PROTOCOL_RELATIVE_REGEX = /^\/\/[^/]+/;
|
|
function hasProtocol(inputString, acceptProtocolRelative = false) {
|
|
return PROTOCOL_REGEX.test(inputString) || acceptProtocolRelative && PROTOCOL_RELATIVE_REGEX.test(inputString);
|
|
}
|
|
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
function hasTrailingSlash(input = "", queryParameters = false) {
|
|
if (!queryParameters) {
|
|
return input.endsWith("/");
|
|
}
|
|
return TRAILING_SLASH_RE.test(input);
|
|
}
|
|
function withoutTrailingSlash(input = "", queryParameters = false) {
|
|
if (!queryParameters) {
|
|
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || "/";
|
|
}
|
|
if (!hasTrailingSlash(input, true)) {
|
|
return input || "/";
|
|
}
|
|
const [s0, ...s] = input.split("?");
|
|
return (s0.slice(0, -1) || "/") + (s.length > 0 ? `?${s.join("?")}` : "");
|
|
}
|
|
function withTrailingSlash(input = "", queryParameters = false) {
|
|
if (!queryParameters) {
|
|
return input.endsWith("/") ? input : input + "/";
|
|
}
|
|
if (hasTrailingSlash(input, true)) {
|
|
return input || "/";
|
|
}
|
|
const [s0, ...s] = input.split("?");
|
|
return s0 + "/" + (s.length > 0 ? `?${s.join("?")}` : "");
|
|
}
|
|
function hasLeadingSlash(input = "") {
|
|
return input.startsWith("/");
|
|
}
|
|
function withoutLeadingSlash(input = "") {
|
|
return (hasLeadingSlash(input) ? input.slice(1) : input) || "/";
|
|
}
|
|
function withoutBase(input, base) {
|
|
if (isEmptyURL(base)) {
|
|
return input;
|
|
}
|
|
const _base = withoutTrailingSlash(base);
|
|
if (!input.startsWith(_base)) {
|
|
return input;
|
|
}
|
|
const trimmed = input.slice(_base.length);
|
|
return trimmed[0] === "/" ? trimmed : "/" + trimmed;
|
|
}
|
|
function getQuery(input) {
|
|
return parseQuery(parseURL(input).search);
|
|
}
|
|
function isEmptyURL(url) {
|
|
return !url || url === "/";
|
|
}
|
|
function isNonEmptyURL(url) {
|
|
return url && url !== "/";
|
|
}
|
|
function joinURL(base, ...input) {
|
|
let url = base || "";
|
|
for (const index of input.filter((url2) => isNonEmptyURL(url2))) {
|
|
url = url ? withTrailingSlash(url) + withoutLeadingSlash(index) : index;
|
|
}
|
|
return url;
|
|
}
|
|
|
|
function parseURL(input = "", defaultProto) {
|
|
if (!hasProtocol(input, true)) {
|
|
return defaultProto ? parseURL(defaultProto + input) : parsePath(input);
|
|
}
|
|
const [protocol = "", auth, hostAndPath = ""] = (input.replace(/\\/g, "/").match(/([^/:]+:)?\/\/([^/@]+@)?(.*)/) || []).splice(1);
|
|
const [host = "", path = ""] = (hostAndPath.match(/([^#/?]*)(.*)?/) || []).splice(1);
|
|
const { pathname, search, hash } = parsePath(path.replace(/\/(?=[A-Za-z]:)/, ""));
|
|
return {
|
|
protocol,
|
|
auth: auth ? auth.slice(0, Math.max(0, auth.length - 1)) : "",
|
|
host,
|
|
pathname,
|
|
search,
|
|
hash
|
|
};
|
|
}
|
|
function parsePath(input = "") {
|
|
const [pathname = "", search = "", hash = ""] = (input.match(/([^#?]*)(\?[^#]*)?(#.*)?/) || []).splice(1);
|
|
return {
|
|
pathname,
|
|
search,
|
|
hash
|
|
};
|
|
}
|
|
|
|
export { withoutTrailingSlash as a, withoutBase as b, getQuery as g, joinURL as j, withTrailingSlash as w };
|