@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.
241 lines (239 loc) • 11 kB
TypeScript
/*!
* ====================================================
* Rzl Utils-JS.
* ----------------------------------------------------
* Version: 3.11.0.
* Author: Rizalvin Dwiky.
* Repository: https://github.com/rzl-zone/utils-js.
* ====================================================
*/
type ExtractFileNameOptions = {
/** ----------------------------------------------------------
* * ***Indicates whether the input should be treated as a potential domain string.***
* ----------------------------------------------------------
*
* - Behavior when `true`:
* - The `domainName` option is required and must be a string and non-empty string.
* If `domainName` is `undefined`, `null`, or an empty string, a `TypeError` will be thrown.
* - The `domainName` is used to determine if the input is a domain-only string.
* - Returns `null` if the input exactly matches `domainName` or any of its subdomains **and** has no additional path or filename.
* - If the input does not match `domainName` or its subdomains, it will be processed as a regular file-like name.
* - Supports **Unicode/IDN domains** (e.g., `tést-ドメイン.com`) and **ASCII filenames**, mixed safely.
*
* @default false
*/
domainAware?: boolean;
/** ----------------------------------------------------------
* * ***The base domain name used for comparison (e.g., `"example.com"`).***
* ----------------------------------------------------------
*
* - Required when `domainAware` is `true`.
* - Helps differentiate between a domain-only input (ignored) and a standalone file-like string (processed normally).
* - Must be a string and non-empty string. Invalid values (`undefined`, `null`, or empty string) will trigger a `TypeError`.
* - Works with both **ASCII domains** and **Unicode/IDN domains**.
* - Example:
* ```ts *
* // ASCII domain + ASCII filename
* extractFileName("resume.com", {
* domainAware: true,
* domainName: "example.com"
* });
* // ➔ "resume"
* extractFileName("example.com", {
* domainAware: true,
* domainName: "example.com"
* });
* // ➔ null (because input is treated as domain-name)
*
* // Unicode domain + ASCII filename
* extractFileName("tést-ドメイン.com/file.txt", {
* domainAware: true,
* domainName: "ドメイン.com"
* });
* // ➔ "file"
*
* // Unicode domain + Unicode filename
* extractFileName("tést-ドメイン.com/ファイル名.pdf", {
* domainAware: true,
* domainName: "ドメイン.com"
* });
* // ➔ "ファイル名"
*
* // Invalid domainName, will throw TypeError
* extractFileName("resume.com", {
* domainAware: true,
* domainName: ""
* });
* // ➔ TypeError
* ```
*
* @default undefined
*/
domainName?: string;
};
/** ----------------------------------------------------------
* * ***Utility: `extractFileName`.***
* ----------------------------------------------------------
*
* **Extracts the **clean base filename** from nearly any input string, including URLs, local file paths,
* UNC paths, and plain filenames.**
*
* - It automatically safely handles extracts the **base file name** (without extension) from:
* - File system paths (Windows, Unix, UNC)
* - Protocols like http, https, ftp, file, mailto, or custom schemes
* - Percent-encoded, Unicode, and emoji characters
* - Dotfiles, reserved OS names, multi-part extensions
* - Data URIs
* - Optional domain-aware mode to ignore domain-only inputs
* - Plain filenames
*
* - Full support for:
* - Unicode, emoji, percent-encoding
* - Dotfiles (e.g., `.env`, `.gitignore`)
* - Reserved/OS-protected filenames:
* `CON`, `PRN`, `AUX`, `NUL`, `COM1`–`COM9`, `LPT1`–`LPT9`
* - Known multi-part extensions:
* `.tar.gz`, `.tar.bz2`, `.tar.xz`, `.tar.lz`, `.tar.zst`, `.min.js`, `.js.map`, `.log.gz`, `.sql.gz`,
* `.backup.tar`, etc.
* - Data URIs (`data:[mime];base64,...` ➔ payload string)
* - Domain-aware mode (optional)
*
* ----------------------------------------------------------
* - **Behavior / Features**
* - Strips **known extensions**, including multi-part and common double/triple extensions.
* - Leaves unknown/custom extensions intact.
* - Preserves **dotfiles** as-is (leading dot preserved).
* - Returns `null` if:
* - input is `null`, `undefined`, or not a string
* - input is empty, whitespace-only, or only slashes
* - input represents a folder path (trailing slash/backslash, drive/folder only)
* - input is a **domain-only string** in domain-aware mode
* - Normalizes Windows-style backslashes (`\`) internally as `/`.
* - Supports UNC paths, mixed slashes, and Windows drive letters safely.
* - Handles URLs:
* - Ignores query strings (`?v=1.2.3`) and hash fragments (`#section`)
* - Decodes percent-encoded filenames (`my%20file.txt` ➔ `my file.txt`)
* - Supports protocol-relative URLs (`//cdn.example.com/file.jpg`)
* - Supports uncommon/custom protocols (`ftp://`, `file://`, `mailto:`, etc.)
* - Handles **multiple dots**, **trailing dots**, **triple or more extensions**
* - Supports filenames on mixed Unicode/ASCII domains:
* - Domain names can include Unicode characters (IDN / punycode)
* - Filenames may contain ASCII, Unicode, and emoji characters
* - Works correctly when domain is Unicode and filename is ASCII, or vice versa
* - Supports extremely long filenames safely (up to OS limits)
* - Domain-aware mode (`domainAware: true` + `domainName`):
* - Parameter `domainName` must be a string and non-empty string; otherwise a TypeError is thrown.
* - Returns `null` if input equals `domainName` or any subdomain with no file path
* - Extracts filename normally if path/file exists on domain or other domain
* - Safe in Node.js and browsers
*
* ----------------------------------------------------------
* @param {string | null | undefined} input
* URL, file path, or plain filename to extract from.
*
* @param {ExtractFileNameOptions} [options]
* Optional configuration:
* - `domainAware?: boolean` – treat input as a domain string. Requires `domainName` to be a string and non-empty string; otherwise, a TypeError is thrown.
* - `domainName?: string` – base domain for comparison eg (`example.com`), required when `domainAware` is true.
*
* @returns {string | null}
* - Base filename without known extensions
* - Original filename if extension unknown
* - `null` for invalid inputs, folder paths, or domain-only strings
*
* ----------------------------------------------------------
* @example
* ```ts
* // Basic files
* extractFileName("document.pdf"); // ➔ "document"
* extractFileName("/files/archive.tar.gz"); // ➔ "archive"
* extractFileName("C:\\path\\file.txt"); // ➔ "file"
* extractFileName(".env"); // ➔ ".env"
* extractFileName("folder/"); // ➔ null
*
* // Not a file
* extractFileName("not-file"); // ➔ null
* extractFileName("not-file/"); // ➔ null
* extractFileName("/not-file/"); // ➔ null
* extractFileName("/not-file"); // ➔ null
*
* // URLs with queries, hashes, protocols
* extractFileName("https://example.com/file.txt?ver=1"); // ➔ "file"
* extractFileName("https://example.com/archive.tar.gz#part"); // ➔ "archive"
* extractFileName("//cdn.example.com/image.png"); // ➔ "image"
*
*
* // Special protocol handling
* extractFileName("tel:+6212345678"); // ➔ "+6212345678"
* extractFileName("sms:+6212345678"); // ➔ "+6212345678"
* extractFileName("mailto:user@domain.com"); // ➔ "user@domain"
* extractFileName("data:text/plain;base64,SGVsbG8="); // ➔ "SGVsbG8="
* extractFileName("mailto:resume.com"); // ➔ "resume"
* extractFileName("ftp://example.com/image.jpeg"); // ➔ "image"
* extractFileName("ftp://files.example.com/app.min.js"); // ➔ "app.min"
* extractFileName("file:///C:/path/to/document.pdf"); // ➔ "document"
* extractFileName("custom-scheme://example.com/video.mp4"); // ➔ "video"
*
* // Unicode & emoji
* extractFileName("emoji-😊.png"); // ➔ "emoji-😊"
* extractFileName("🔥project.tar.gz"); // ➔ "🔥project"
*
* // Dotfiles
* extractFileName(".gitignore"); // ➔ ".gitignore"
* extractFileName("/path/.bashrc"); // ➔ ".bashrc"
*
* // Mixed Unicode domain and ASCII filename
* extractFileName("https://tést-ドメイン.com/file.txt"); // ➔ "file"
* extractFileName("https://example.com/ファイル名.pdf"); // ➔ "ファイル名"
* extractFileName("https://ドメイン例.com/emoji-🔥.png"); // ➔ "emoji-🔥"
*
* // Reserved filenames
* extractFileName("CON"); // ➔ "CON"
* extractFileName("NUL.txt"); // ➔ "NUL"
*
* // Domain-aware mode
* extractFileName("example.com", {
* domainAware: true,
* domainName: "example.com"
* });
* // ➔ null
* extractFileName("cdn.example.com", {
* domainAware: true,
* domainName: "example.com"
* });
* // ➔ null
* extractFileName("resume.com", {
* domainAware: true,
* domainName: "example.com"
* });
* // ➔ "resume"
* extractFileName("https://example.com/file.txt", {
* domainAware: true,
* domainName: "example.com"
* });
* // ➔ "file"
*
* // Windows & UNC paths
* extractFileName("C:\\Users\\rzl\\Documents\\file.txt"); // ➔ "file"
* extractFileName("\\\\SERVER\\share\\logs\\output.log"); // ➔ "output"
* extractFileName("C:/Users\\rzl/mix\\test.pdf"); // ➔ "test"
*
* // Edge / extreme cases
* extractFileName("https://example.com/my%20file%20name.txt"); // ➔ "my file name"
* extractFileName("app.min.js.map"); // ➔ "app.min"
* extractFileName("backup.tar.bak"); // ➔ "backup.tar.bak" (unknown double extension)
* extractFileName("filename."); // ➔ "filename."
* extractFileName("a".repeat(255) + ".txt"); // ➔ "a".repeat(255)
* ```
*
* ----------------------------------------------------------
* @note
* - Robust: never throws, handles unusual inputs safely.
* - Suitable for display, logging, or safe storage.
* - Normalizes slashes consistently.
* - Covers nearly all real-world filename, URL, path, data URI, and domain scenarios.
* - Handles Windows UNC paths, mixed slashes, percent-encoded, Unicode/emoji filenames.
* - Known multi-part extensions list can be extended without breaking functionality.
*/
declare const extractFileName: (input?: string | null, options?: ExtractFileNameOptions) => string | null;
export { type ExtractFileNameOptions, extractFileName };