UNPKG

build-url-ts

Version:

A small library that builds a URL given its components

153 lines (151 loc) 6.08 kB
/** * `build-url-ts` — a small, fast, zero-dependency library for composing URLs * from their parts (path, query string, and hash fragment). * * It runs anywhere JavaScript does (Node.js, Bun, Deno, edge runtimes, and * browsers) and ships both ESM and CommonJS builds with full type definitions. * * @packageDocumentation * @example * ```ts * import { buildUrl } from 'build-url-ts'; * * buildUrl('https://api.example.com', { * path: 'users/123', * queryParams: { tab: 'profile', limit: 10 }, * hash: 'summary', * }); * // → https://api.example.com/users/123?tab=profile&limit=10#summary * ``` */ /** * A value accepted for a single query parameter. * * - `string` / `number` / `boolean` are stringified as-is. * - `null` becomes an empty value (`key=`). * - `undefined` is omitted entirely. * - An array is rendered as a comma-separated list by default, or as repeated / * indexed keys via {@link IBuildUrlOptions.disableCSV}. * - A `Date` is serialized with `Date.prototype.toString()`. * - Any other `object` is serialized with `JSON.stringify()`. */ type QueryParamValue = null | undefined | string | number | boolean | (string | number | boolean | null | undefined)[] | Date | object; /** A map of query parameter names to {@link QueryParamValue}s. */ type IQueryParams = Record<string, QueryParamValue>; /** * How array query parameters are serialized when CSV joining is disabled. * * - `'array'` → `key[]=a&key[]=b` * - `'order_asc'` → `key[0]=a&key[1]=b` * - `'order_desc'` → `key[1]=a&key[0]=b` * * The boolean `true` (see {@link IBuildUrlOptions.disableCSV}) produces repeated * keys without brackets: `key=a&key=b`. */ type IDisableCsvType = 'array' | 'order_asc' | 'order_desc'; /** Options describing the parts of the URL to build. */ interface IBuildUrlOptions { /** * A single path segment appended to the URL. Leading, trailing, and duplicate * slashes are normalized. * * @example 'about/me' // → /about/me */ path?: string | number; /** * Multiple path segments appended in order. Use this instead of (or in * addition to) {@link IBuildUrlOptions.path}; when both are given, `path` is * applied first, then each entry of `paths`. * * @example ['about', '/my/', '/cat'] // → /about/my/cat */ paths?: (string | number)[]; /** Lowercase the generated path, query string, and hash. Defaults to `false`. */ lowerCase?: boolean; /** Query parameters to append, merged on top of any already present on the URL. */ queryParams?: IQueryParams; /** * Control how array query parameters are rendered. `false`/omitted joins them * into a comma-separated list; `true` repeats the key; a {@link IDisableCsvType} * selects a bracketed format. */ disableCSV?: boolean | IDisableCsvType; /** Hash/fragment identifier to append (without the leading `#`). */ hash?: string | number; } /** * Builds a query string (including the leading `?`) from a parameters object. * * @param queryParams - The parameters to serialize. * @param lowerCase - Lowercase keys and values. Defaults to `false`. * @param disableCSV - How to render array values. See {@link IDisableCsvType}. * @param useCustomEncoding - Use {@link customEncodeURIComponent} (escapes `'` * and `` ` ``) instead of plain `encodeURIComponent`. Defaults to `true`. * {@link buildUrl} passes `false` because it encodes values itself. * @returns The query string (e.g. `?foo=bar&bar=baz`), or `''` when empty. * * @example * ```ts * buildQueryString({ foo: 'bar', ids: [1, 2, 3] }); * // → ?foo=bar&ids=1%2C2%2C3 * ``` */ declare function buildQueryString(queryParams: IQueryParams, lowerCase?: boolean, disableCSV?: boolean | IDisableCsvType, useCustomEncoding?: boolean): string; /** * Appends a single path segment to a URL, normalizing slashes so there are no * empty or doubled segments while any meaningful trailing slash is preserved. * * @param path - The segment to append. * @param builtUrl - The URL built so far. * @param lowerCase - Lowercase the segment. Defaults to `false`. * @returns The URL with the segment appended. * * @example * ```ts * appendPath('users/123', 'https://api.example.com'); * // → https://api.example.com/users/123 * ``` */ declare function appendPath(path: string | number, builtUrl: string, lowerCase?: boolean): string; /** * Builds a hash fragment (including the leading `#`) from a value. * * @param hash - The fragment text (without `#`). * @param lowerCase - Lowercase the fragment. Defaults to `false`. * @returns The hash fragment (e.g. `#section`), or `''` when empty. * * @example * ```ts * buildHash('Section-1', true); // → #section-1 * ``` */ declare function buildHash(hash: string | number, lowerCase?: boolean): string; /** * Builds a complete URL from a base URL and/or a set of options. * * Query parameters and hash already present on the base URL are preserved and * merged with the supplied options (options take precedence on conflicts). * * @param url - The base URL, or — when called with a single argument — the * options object itself. `null`/`undefined` builds a relative URL. * @param options - The parts to add. See {@link IBuildUrlOptions}. * @returns The constructed URL string. * * @example * ```ts * // Base URL plus options * buildUrl('https://example.com', { path: 'about', hash: 'team' }); * // → https://example.com/about#team * * // Options only (relative URL) * buildUrl({ path: 'api/v2', queryParams: { format: 'json' } }); * // → /api/v2?format=json * * // Multiple path segments * buildUrl('https://example.com', { paths: ['about', '/my/', '/cat'] }); * // → https://example.com/about/my/cat * ``` */ declare function buildUrl(url?: string | null | IBuildUrlOptions, options?: IBuildUrlOptions): string; export { appendPath, buildHash, buildQueryString, buildUrl, buildUrl as default }; export type { IBuildUrlOptions, IDisableCsvType, IQueryParams, QueryParamValue };