UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

58 lines (57 loc) 3.39 kB
import { RequiredError } from "../error/RequiredError.js"; import { isAbsolutePath } from "./path.js"; import { isURI } from "./uri.js"; import { getBasedURI, getURL } from "./url.js"; /** * Resolve a possible link to an absolute URI string, or return `undefined` if resolution fails. * * Classification: * - **`URL` instance** → returns its `.href` directly. * - **Absolute path** (single leading `/`, e.g. `/schema`) → resolved against `root` with a dot prefix so the resolution honors `root`'s subfolder — `/schema` against `https://x.com/app/` becomes `https://x.com/app/schema`, not `https://x.com/schema`. * - **Anything else** — relative ref (`./foo`, `../foo`, `foo`, `#anchor`, `?q`), protocol-relative URL (`//host/path`), or scheme-prefixed URI (`mailto:a@b`, `tel:123`, `https://x.com/y`) → fed to `getBasedURI` with `url` as the base. Self-contained URIs ignore the base (their own scheme makes them absolute); protocol-relative and relative refs resolve against the page URL. * * Defaults: * - `root` defaults to `url`, so passing only `url` makes absolute paths resolve under the page URL's directory (same coordinate space the page lives in). * - When both are omitted, all branches return `undefined` (no base to resolve against). * * Bases are passed through to `getURL` / `getBasedURI` lazily — neither `url` nor `root` is materialised into a normalised base until the chosen branch needs it. * * @param href The link to resolve. Strings are classified by shape; `URL` instances pass through. * @param url The current page URL — base for relative refs and scheme-prefixed URIs. * @param root The site root URL — base for absolute paths. Defaults to `url`. * @returns An absolute URI object, or `undefined` if `link` is missing, not a string/URL, or cannot be resolved. * * @example getLink("/schema", pageURL, siteRoot) // → "https://x.com/app/schema" when siteRoot is "https://x.com/app/" * @example getLink("./db", new URL("https://x.com/app/schema/")) // → "https://x.com/app/schema/db" * @example getLink("mailto:a@b") // → "mailto:a@b" */ export function getLink(href, url, root = url) { if (!href) return; if (isURI(href)) return href; if (typeof href !== "string") return; // Single leading slash is a site-absolute path; `//` is a protocol-relative URL and falls through to `getBasedURI`. if (isAbsolutePath(href) && !href.startsWith("//")) return getURL(`.${href}`, root); return getBasedURI(href, url ?? root); } /** * Resolve a possible link to an absolute URI string, or throw `RequiredError` if resolution fails. * * Same classification and defaults as `getLink`. Use when an absolute URI is required and there's no sensible "do nothing" path for the caller. * * @param href The link to resolve. * @param url The current page URL — base for relative refs and scheme-prefixed URIs. * @param root The site root URL — base for absolute paths. Defaults to `url`. * @param caller Identity of the calling function for error attribution. * @returns An absolute URI string. * @throws `RequiredError` if `link` cannot be resolved. */ export function requireLink(href, url, root, caller = requireLink) { const uri = getLink(href, url, root); if (!uri) throw new RequiredError("Invalid link", { received: href, caller }); return uri; }