/** 去掉尾部 /(根路径除外处理后为空串)。 */ export function stripTrailingSlash(url: string): string { if (url === "/" || url === "") return url; return url.replace(/\/+$/, ""); } /** 确保 base 不带尾部 /。 */ export function normalizeSiteUrl(siteUrl: string): string { return stripTrailingSlash(siteUrl.trim()); } /** * 将站内 path 或绝对 URL 解析为绝对地址。 * - 已是 http(s) → 规范化后原样返回 * - 以 / 开头 → 拼到 siteUrl * - 相对路径 → 补 / */ export function absoluteUrl(siteUrl: string, pathOrUrl: string): string { const base = normalizeSiteUrl(siteUrl); const raw = pathOrUrl.trim(); if (/^https?:\/\//i.test(raw)) { return raw; } const path = raw.startsWith("/") ? raw : `/${raw}`; return new URL(path, `${base}/`).toString(); } /** 路径规范化:保证以 / 开头、去掉多余尾 /(根除外)。 */ export function normalizePath(path: string): string { const raw = path.trim() || "/"; const withSlash = raw.startsWith("/") ? raw : `/${raw}`; if (withSlash === "/") return "/"; return stripTrailingSlash(withSlash); } /** 从绝对 URL 取 pathname(含查询则保留)。 */ export function urlPathname(absOrPath: string): string { if (/^https?:\/\//i.test(absOrPath)) { const u = new URL(absOrPath); return `${u.pathname}${u.search}`; } return normalizePath(absOrPath); }