UNPKG

@rzl-zone/utils-js

Version:

A modern, lightweight set of JavaScript utility functions with TypeScript support for everyday development, crafted to enhance code readability and maintainability.

232 lines (226 loc) 12.1 kB
/*! * ==================================================== * Rzl Utils-JS. * ---------------------------------------------------- * Version: 3.11.0. * Author: Rizalvin Dwiky. * Repository: https://github.com/rzl-zone/utils-js. * ==================================================== */ import { IsAny } from '@rzl-zone/ts-types-plus'; /** --------------------------------------------------------- * * ***Extracts dynamic route parameters from a given route string.*** * --------------------------------------------------------- * **This utility type recursively searches for dynamic segments within a route, * extracting each parameter and constructing an object where each key represents * a dynamic segment and its value is of type `string`.** * - ***⚠️ Warning:*** * - ***This types only support when using ***[`NextJS`](https://nextjs.org/)***.*** * @template T - The route string containing potential dynamic segments. * @example * ```ts * type Params1 = ExtractRouteParams<"/user/[id]">; * // ➔ { id: string } * type Params2 = ExtractRouteParams<"/post/[slug]/comment/[commentId]">; * // ➔ { slug: string; commentId: string } * type Params3 = ExtractRouteParams<"/dashboard">; * // ➔ {} (no dynamic parameters) * ``` */ type ExtractRouteParams<T> = T extends string ? HasDynamicSegments<T> extends true ? T extends `${infer _Start}[${infer Param}]${infer Rest}` ? { [K in Param | keyof ExtractRouteParams<Rest>]: string; } : unknown : unknown : unknown; /** --------------------------------------------------------- * * ***Determines whether a given route contains dynamic segments.*** * --------------------------------------------------------- * **This type checks if the route includes at least one `[param]` pattern. * If it does, the result is `true`, otherwise `false`.** * - ***⚠️ Warning:*** * - ***This types only support when using ***[`NextJS`](https://nextjs.org/)***.*** * @template T - The route string to be evaluated. * @example * ```ts * type HasParams1 = HasDynamicSegments<"/user/[id]">; * // ➔ true * type HasParams2 = HasDynamicSegments<"/settings/profile">; * // ➔ false * type HasParams3 = HasDynamicSegments<"/blog/[category]/[slug]">; * // ➔ true * ``` */ type HasDynamicSegments<T> = T extends `${string}[${string}]${string}` ? true : false; type GenerateRouteResult<T> = true extends IsAny<T> ? unknown : T extends string ? string : unknown; /** --------------------------------- * * ***Utility for NextJS: `generateRoute`.*** * --------------------------------- * **Generates a URL by replacing dynamic route parameters with provided values.** * - ***⚠️ Warning:*** * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.*** * @template T - The route string containing dynamic segments in the format `[param]`. * @param {T} route - The route string containing dynamic segments. * @param {ExtractRouteParams<T>} [params] - An object containing key-value pairs that match the dynamic segments in the route. * @returns {string} The formatted URL with all dynamic segments replaced. * @throws **{@link Error | `Error`}** if the route contains dynamic segments but no parameters object is provided. * @throws **{@link Error | `Error`}** if a required parameter is missing from the `params` object. * @throws **{@link Error | `Error`}** if a parameter value is an empty string. * @throws **{@link Error | `Error`}** if any parameter contains invalid characters like `?`, `&`, `=`, `#`, `/`, spaces, `'`, `"`, `(`, `)`, `+`, `;`, `%`, `@`, or `:`, which can cause URL issues. * @example * // Basic usage * generateRoute("/user/[id]", { id: "123" }); * // ➔ "/user/123" * * // No dynamic segments, returns as-is * generateRoute("/dashboard"); * // ➔ "/dashboard" * * // Throws an error due to missing parameters object * generateRoute("/profile/[username]"); * // ➔ ❌ Error: ❌ Missing parameters object for route: "/profile/[username]" * * // Throws an error due to an empty parameter value * generateRoute("/post/[category]/[slug]", { category: "tech", slug: "" }); * // ➔ ❌ Error: ❌ Parameter "slug" cannot be empty in route: "/post/[category]/[slug]" * * // Throws an error due to parameter containing invalid characters * generateRoute("/search/[query]", { query: "how to?learn" }); * // ➔ ❌ Error: ❌ Parameter "query" contains invalid character "?" in route: "/search/[query]" * * // Handles leading/trailing slashes correctly * generateRoute("/blog/[category]/[slug]", { category: "/news/", slug: "/latest-update/" }); * // ➔ ❌ Error: ❌ Parameter "category" and "slug" contains slashes "/" which is not allowed. */ declare function generateRoute<T extends string>(route: T extends string ? (HasDynamicSegments<T> extends true ? T : never) : never, params: T extends string ? ExtractRouteParams<T> : undefined): GenerateRouteResult<T>; declare function generateRoute<T extends string>(route: T extends string ? T : never, params?: Extract<ExtractRouteParams<T>, Record<string, unknown>>): GenerateRouteResult<T>; declare function generateRoute<T = unknown>(route: T extends string ? (HasDynamicSegments<T> extends true ? T : unknown) : unknown, params?: T extends string ? ExtractRouteParams<T> : undefined): unknown; type OptionsCreateBeApiUrl = { /** * The prefix pathname api url, e.g:`"http://localhost.com/your-target-prefix-entri-point-api-is-here"`, default: `"/api"`. * * @default "/api" */ prefix?: string; /** * Option to getting `prefix` and `pathname` of api url only `(removing origin base api url)`, default: `true`. * * @default true */ withOrigin?: boolean; }; /** --------------------------------- * * ***Utility for NextJS: `createBeApiUrl`.*** * --------------------------------- * **Constructs a backend API URL by appending a given pathname to the base API URL.** * - **ℹ️ Note:** * - This function builds on top of `getBeApiUrl()`. * - **Determines the base API URL from:** * - `NEXT_PUBLIC_BACKEND_API_URL` environment variable (or defaults to `"http://localhost:8000"`). * - Automatically appends `NEXT_PUBLIC_PORT_BE` if the base URL does not already include a port. * - **Features of this function:** * - Allows customizing the API path with an optional `prefix` (defaults to `"/api"`). * - Can include or exclude the origin (protocol + host) via `withOrigin`. * - Normalizes paths to avoid duplicate slashes. * - ***⚠️ Warning:*** * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.*** * @param {string|null|undefined} pathname - The API endpoint path (e.g., `/users` or `/v1/posts`), defaultValue: `""`. * @param {OptionsCreateBeApiUrl} [options] - Configuration options. * @param {OptionsCreateBeApiUrl["prefix"]} [options.prefix="/api"] - The prefix for the API path (default is `"/api"`). * @param {OptionsCreateBeApiUrl["withOrigin"]} [options.withOrigin=true] - Whether to include the full base URL or return only the API path. * @returns {string} The formatted API URL. * @throws **{@link TypeError | `TypeError`}** if `withOrigin` is not a boolean. * @throws **{@link TypeError | `TypeError`}** if `prefix` and `pathname` is not a string. * @throws **{@link Error | `Error`}** if constructing the API URL fails due to an invalid base URL. * @example * createBeApiUrl("/users") * // ➔ "http://localhost:8000/api/users" * createBeApiUrl("/api/users") * // ➔ "http://localhost:8000/api/users" * createBeApiUrl("/v1", { prefix: "/v1" }) * // ➔ "http://localhost:8000/v1" * createBeApiUrl("/v1/users") * // ➔ "http://localhost:8000/api/v1/users" * createBeApiUrl("/v1/users", { prefix: "/v1" }) * // ➔ "http://localhost:8000/v1/users" * createBeApiUrl("/users", { withOrigin: false }) * // ➔ "/api/users" * createBeApiUrl(null, { withOrigin: false }) * // ➔ "/api" * createBeApiUrl(undefined, { withOrigin: false }) * // ➔ "/api" */ declare const createBeApiUrl: ( /** * The pathname api url, e.g:`"http://localhost.com/your-target-prefix-entri-point-api-is-here/your-target-pathname-is-here"`. * * @default "" */ pathname: string | null | undefined, options?: OptionsCreateBeApiUrl) => string; type OptionsGetBeApiUrl = { /** * ***The Suffix origin base api url, e.g:`http://localhost.com/api`, default: `"/"`.*** * * @default "/" */ suffix?: string; }; /** --------------------------------------------------- * * ***Utility for NextJS: `getBeApiUrl`.*** * --------------------------------------------------- * **This function determines the backend API base URL from the `NEXT_PUBLIC_BACKEND_API_URL` environment variable (retrieves the base API URL of the backend).** * - **Behavior:** * - If the variable is not set, it defaults to `"http://localhost:8000"`. * - It also allows adding an optional suffix to the returned URL. * - ***⚠️ Warning:*** * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.*** * @description * This function determines the backend API base URL from the `NEXT_PUBLIC_BACKEND_API_URL` environment variable. * - If `NEXT_PUBLIC_BACKEND_API_URL` is not set, it defaults to `"http://localhost:8000"`. * - If `NEXT_PUBLIC_BACKEND_API_URL` does **not** contain a port, it appends one from `NEXT_PUBLIC_PORT_BE` if available. * - Supports appending optional suffix (like `"/api"`). * @param {OptionsGetBeApiUrl|undefined} options - Configuration options. * @param {OptionsGetBeApiUrl["suffix"]} [options.suffix="/"] - The suffix to append to the base API URL. * @returns {string} The formatted backend API base URL. * @throws **{@link TypeError | `TypeError`}** if `suffix` is not a `string`. * @throws **{@link Error | `Error`}** if `NEXT_PUBLIC_BACKEND_API_URL` is invalid. * @example * // With NEXT_PUBLIC_BACKEND_API_URL set at `*.env` file * NEXT_PUBLIC_BACKEND_API_URL = "https://api.example.com"; * getBeApiUrl(); * // ➔ "https://api.example.com/" * * // With NEXT_PUBLIC_BACKEND_API_URL but no port, using NEXT_PUBLIC_PORT_BE at `*.env` file * NEXT_PUBLIC_BACKEND_API_URL = "http://localhost"; * NEXT_PUBLIC_PORT_BE = "5000"; * getBeApiUrl({ suffix: "/api" }); * // ➔ "http://localhost:5000/api" * * // Without NEXT_PUBLIC_BACKEND_API_URL at `*.env` file (defaults to localhost:8000) * delete NEXT_PUBLIC_BACKEND_API_URL; * getBeApiUrl({ suffix: "/v1" }); * // ➔ "http://localhost:8000/v1" */ declare const getBeApiUrl: (options?: OptionsGetBeApiUrl) => string; /** --------------------------------------------------- * * ***Utility for NextJS: `getBaseUrl`.*** * --------------------------------------------------- * **Retrieves the base URL of the application.** * - **Behavior:** * - It determines the base URL from the `NEXT_PUBLIC_BASE_URL` environment variable. * - If `NEXT_PUBLIC_BASE_URL` is not set, it defaults to `"http://localhost:3000"`. * - If `NEXT_PUBLIC_BASE_URL` does **not** contain a port, it appends one from `NEXT_PUBLIC_PORT_FE` if available. * - Ensures the final URL is valid and normalized (no trailing slashes). * - ***⚠️ Warning:*** * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.*** * @returns {string} The resolved base URL of the application. * @throws **{@link Error | `Error`}** if the constructed URL is invalid or malformed. * @example * // With environment variable set at `*.env` file * NEXT_PUBLIC_BASE_URL = "https://example.com"; * getBaseUrl(); * // ➔ "https://example.com" * * // With custom port via NEXT_PUBLIC_PORT_FE at `*.env` file * NEXT_PUBLIC_BASE_URL = "http://localhost"; * NEXT_PUBLIC_PORT_FE = "4000"; * getBaseUrl(); * // ➔ "http://localhost:4000" * * // Without environment variable at `*.env` file * delete NEXT_PUBLIC_BASE_URL; * getBaseUrl(); * // ➔ "http://localhost:3000" */ declare const getBaseUrl: () => string; export { type ExtractRouteParams, type HasDynamicSegments, createBeApiUrl, generateRoute, getBaseUrl, getBeApiUrl };